Coincidencia de una búsqueda con una expresión regular y cuatro líneas de contexto

7 minutos de lectura

avatar de usuario
usuario1818018

Por ejemplo, tengo este tipo de código:

<?php
/**
 * Order
 *
 * The WooCommerce order class handles order data.
 *
 * @class       WC_Order
 * @version     1.6.4
 * @package     WooCommerce/Classes
 * @category    Class
 * @author      WooThemes
 */
class WC_Order {

    /** @public int Order (post) ID */
    public $id;

    /** @public string Order status. */
    public $status;

    /** @public string Order date (placed). */
    public $order_date;

    /** @public string Order date (paid). */
    public $modified_date;

    /** @public string Note added by the customer. */
    public $customer_note;

    /** @public array Order (post) meta/custom fields. */
    public $order_custom_fields;

        global $wpdb, $woocommerce;

        if ( empty( $type ) )
            $type = array( 'line_item' );

        if ( ! is_array( $type ) )
            $type = array( $type );

        $items = $this->get_items( $type );

        $count = 0;

        foreach ( $items as $item ) {
            if ( ! empty( $item['qty'] ) )
                $count += $item['qty'];
            else
                $count ++;
        }

        return apply_filters( 'woocommerce_get_item_count', $count, $type, $this );
    }

    /**
     * Return an array of fees within this order.
     *
     * @access public
     * @return array
     */
    public function get_fees() {
        return $this->get_items( 'fee' );
    }

    /**
     * Return an array of taxes within this order.
     *
     * @access public
     * @return void
     */
    public function get_taxes() {
        return $this->get_items( 'tax' );
    }

    /**
     * Get taxes, merged by code, formatted ready for output.
     *
     * @access public
     * @return void
     */
    public function get_tax_totals() {
        $taxes      = $this->get_items( 'tax' );
        $tax_totals = array();

        foreach ( $taxes as $key => $tax ) {

            $code = $tax[ 'name' ];

            if ( ! isset( $tax_totals[ $code ] ) ) {
                $tax_totals[ $code ] = new stdClass();
                $tax_totals[ $code ]->amount = 0;
            }

            $tax_totals[ $code ]->is_compound       = $tax[ 'compound' ];
            $tax_totals[ $code ]->label             = isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ];
            $tax_totals[ $code ]->amount           += $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ];
            $tax_totals[ $code ]->formatted_amount  = woocommerce_price( $tax_totals[ $code ]->amount );
        }

        return apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this );
    }

    /**
     * has_meta function for order items.
     *
     * @access public
     * @return array of meta data
     */
    public function has_meta( $order_item_id ) {
        global $wpdb;

        return $wpdb->get_results( $wpdb->prepare("SELECT meta_key, meta_value, meta_id, order_item_id
            FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = %d
            ORDER BY meta_key,meta_id", absint( $order_item_id ) ), ARRAY_A );
    }

    /**
     * Get order item meta.
     *
     * @access public
     * @param mixed $item_id
     * @param string $key (default: '')
     * @param bool $single (default: false)
     * @return void
     */
    public function get_item_meta( $order_item_id, $key = '', $single = false ) {
        return get_metadata( 'order_item', $order_item_id, $key, $single );
}

Quiero hacer coincidir todos los ganchos de WordPress: “do_action” y “apply_filters” con tres opciones: apply_filters (‘woocommerce_order_tax_totals’, $tax_totals, $this), archivo, número de línea

Un ejemplo de lo que estoy tratando de hacer se puede ver aquí:
http://etivite.com/api-hooks/buddypress/trigger/apply_filters/bp_get_total_mention_count_for_user/

http://adambrown.info/p/wp_hooks/hook/activated_plugin?version=3.6&file=wp-admin/includes/plugin.php

Intenté sacar algo pero sin éxito:

<?php
$path = $_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/iphorm-form-builder';
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $filename) {
    if (substr($filename, -3) == 'php') {
        $file = file($filename);
        if ($file !== false) {
            $matches1 = preg_grep( '/do_action\((.+)\);/', $file);
            $matches2 = preg_grep( '/apply_filters\((.+)\);/', $file );
            $arr = array_filter(array_merge($matches1, $matches2));
            $out="";
            echo "found in $filename:";
            echo "<pre>";
            foreach ($arr as $key => $value) {
                $out .= $file[$key-2];
                $out .= $file[$key-1];
                $out .= $file[$key];
                $out .= $file[$key+1];
                $out .= $file[$key+2];
            }
            echo htmlentities($out);
            echo "</pre>";
        } else {
            echo "failed reading to array";
        }
    }
}

  • Consulte este hilo para obtener algunos consejos sobre cómo obtener más atención para preguntas antiguas: meta.stackexchange.com/questions/7046/…

    – Adán Lear

    18/09/2013 a las 15:30


  • ¿Cuál es el resultado esperado para el código de ejemplo que ha dado?

    -Joe Frambach

    18 de septiembre de 2013 a las 16:16

  • Ah, creo que lo entiendo. La terminología que necesita es “contexto”. Quiere una línea emparejada, con 4 líneas de contexto.

    -Joe Frambach

    18/09/2013 a las 20:26

  • WTF?? global $wpdb, $woocommerce; en una clase

    – Elías Van Ootegem

    20 de septiembre de 2013 a las 12:13

  • @EliasVanOotegem bienvenido a wordpress. Comprueba tu cordura en la puerta.

    -Joe Frambach

    20/09/2013 a las 15:25

avatar de usuario
joe frambach

Esto se puede hacer de manera muy simple aprovechando los comandos de shell incorporados.

<?php
$path = $_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/iphorm-form-builder';
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $filename) {
    if (substr($filename, -3) == 'php') {
        $context = shell_exec('grep -H -n -C4 do_action ' . escapeshellarg($filename));
        if (!empty($context)) {
            echo "found in $filename:";
            echo "<pre>";
            echo htmlentities($context);
            echo "</pre>";
        } else {
            echo "failed reading to array";
        }
    }
}

Documentación relevante:

php shell_exec

Ejecute el comando a través de Shell y devuelva la salida completa como una cadena

php escapeshellarg

Escape de una cadena para usarla como argumento de shell

bash grep

   Grep  searches the named input FILEs (or standard input if no files are
   named, or the file name - is given) for lines containing a match to the
   given PATTERN.  By default, grep prints the matching lines.

   -C NUM, --context=NUM
      Print  NUM lines of output context.  Places a line containing --
      between contiguous groups of matches.

   -H, --with-filename
      Print the filename for each match.

   -n, --line-number
      Prefix each line of output with the line number within its input
      file.

Editar

Dependiendo de cuántos directorios y archivos tenga en su proyecto, es posible que no funcione. Básicamente, está creando un nuevo proceso en un nuevo shell para cada archivo. Eso no es genial. Si prefiere obtener una gran cantidad de datos y analizarlos más tarde, haga esto en su lugar:

<?php
$path = $_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/iphorm-form-builder';
$grep = shell_exec('grep --include=*.php -RHn -C4 do_action ' . escapeshellarg($path));
$matches = explode("\n--\n", $grep);
if (empty($matches)) {
    echo "failed reading to array";
}
else {
    foreach ($matches as $match) {
        echo "<pre>";
        echo htmlentities($match);
        echo "</pre>";
    }
}

  • Vaya, esto es genial, estoy creando un complemento ahora mismo 🙂 El directorio de contenido puede estar en cualquier lugar, si se usa en el contexto de WP, esto es más a prueba de balas: $path = WP_CONTENT_DIR . '/plugins/plugin-name';

    – brasofilo

    20 de septiembre de 2013 a las 16:38


  • Hola Joe, perdón por la demora, no estuve cerca de una computadora por mucho tiempo 🙂 ¡Muchas gracias!

    – usuario1818018

    6 oct 2013 a las 9:33


avatar de usuario
anubhava

No necesita recorrer los datos del archivo línea por línea. Simplemente lea los datos en una variable y aplique expresiones regulares:

$data = file_get_contents( $filename );
if (preg_match_all(
      '~((?:[^\n]*\n){0,4}) *do_action\s*\(\s*([^)]+)\s*\)\s*;[^\n]*\n((?:[^\n]*\n){0,4})~',
       $data, $arr)) {
   // match found, now dump the results
   // $arr[1] will print 4 lines before the match
   // $arr[2] will print function arguments for do_action
   // $arr[3] will print 4 lines after the match
   print_r($arr);
}

  • @JoeFrambach: Sí, de hecho $arr[3] will print 4 lines after the match y tiene razón sobre las primeras 4 líneas del archivo, verifique la edición ahora.

    – anubhava

    18/09/2013 a las 20:38

avatar de usuario
Daniel Giménez

Esto no se puede hacer solo con expresiones regulares dadas las limitaciones del motor de expresiones regulares de PHP. Específicamente, y me sorprendió, no puedes tener traseros de longitud variable.

La razón por la que necesita mirar atrás es si tuvo la ocurrencia de hacer_acción o aplicar_filtros en líneas consecutivas, entonces la primera coincidencia evitará la segunda coincidencia si no tiene una mirada hacia adelante, e incluso si usa una mirada hacia adelante, no hay forma de obtener las dos filas anteriores de la segunda coincidencia sin la mirada hacia atrás. Sin mencionar que ni siquiera sé si puede incluir el contenido de una afirmación de mirar alrededor en el resultado.

Esta solución crea una expresión regular para encontrar las líneas en las que ocurre la función y luego usa dos expresiones regulares más para encontrar las líneas antes y después. Tuve cuidado de tener cuidado con el inicio y el final del archivo al diseñar las expresiones regulares.

$offset = 0;
while (preg_match('/^.*?(?:apply_filters|do_action)\s*\(.+?\)\s*;.*(?:\n\r|\n|\r|$)/m', $file, $match, PREG_OFFSET_CAPTURE, $offset))
{
    $index = $match[0][1];
    $offset = $index + strlen($match[0][0]);
    $hasBefore = preg_match('/(?:.*(?:\n\r|\n|\r).*$)|[^\n\r].*$/', 
        substr($file, 0, $index), $beforeMatch);
    $hasAfter = preg_match('/^(?:.*(?:\n\r|\n|\r|$)){0,2}/', 
        substr($file, $offset), $afterMatch);

    if ($hasBefore) print_r($beforeMatch);
    print_r($match[0][0]);
    if ($hasAfter) print_r($afterMatch);
}

phpviolín

Esto solo muestra dos líneas antes y dos líneas después. Puede usar la repetición si quiere más, pero me parece, por el intento de solución, que esto era lo que realmente quería el operador.

¿Ha sido útil esta solución?