|
| 1 | +<?php |
| 2 | +/** |
| 3 | + * Generate documentation for hooks in WC |
| 4 | + */ |
| 5 | +class WC_HookFinder { |
| 6 | + const SOURCE_PATH = '../source/woocommerce/'; |
| 7 | + |
| 8 | + private static $current_file = ''; |
| 9 | + private static $files_to_scan = array(); |
| 10 | + private static $pattern_custom_actions = '/do_action(.*?);/i'; |
| 11 | + private static $pattern_custom_filters = '/apply_filters(.*?);/i'; |
| 12 | + private static $found_files = array(); |
| 13 | + private static $custom_hooks_found = ''; |
| 14 | + |
| 15 | + private static function get_files( $pattern, $flags = 0, $path = '' ) { |
| 16 | + |
| 17 | + if ( ! $path && ( $dir = dirname( $pattern ) ) != '.' ) { |
| 18 | + |
| 19 | + if ( '\\' == $dir || '/' == $dir ) { |
| 20 | + $dir = ''; |
| 21 | + } |
| 22 | + |
| 23 | + return self::get_files( basename( $pattern ), $flags, $dir . '/' ); |
| 24 | + |
| 25 | + } // End IF Statement |
| 26 | + |
| 27 | + $paths = glob( $path . '*', GLOB_ONLYDIR | GLOB_NOSORT ); |
| 28 | + $files = glob( $path . $pattern, $flags ); |
| 29 | + |
| 30 | + if ( is_array( $paths ) ) { |
| 31 | + foreach ( $paths as $p ) { |
| 32 | + $found_files = array(); |
| 33 | + $retrieved_files = (array) self::get_files( $pattern, $flags, $p . '/' ); |
| 34 | + foreach ( $retrieved_files as $file ) { |
| 35 | + if ( ! in_array( $file, self::$found_files ) ) { |
| 36 | + $found_files[] = $file; |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | + self::$found_files = array_merge( self::$found_files, $found_files ); |
| 41 | + |
| 42 | + if ( is_array( $files ) && is_array( $found_files ) ) { |
| 43 | + $files = array_merge( $files, $found_files ); |
| 44 | + } |
| 45 | + } // End FOREACH Loop |
| 46 | + } |
| 47 | + return $files; |
| 48 | + } |
| 49 | + |
| 50 | + private static function get_hook_link( $hook, $details = array() ) { |
| 51 | + if ( ! empty( $details['class'] ) ) { |
| 52 | + $link = 'http://docs.woocommerce.com/wc-apidocs/source-class-' . $details['class'] . '.html#' . $details['line']; |
| 53 | + } elseif ( ! empty( $details['function'] ) ) { |
| 54 | + $link = 'http://docs.woocommerce.com/wc-apidocs/source-function-' . $details['function'] . '.html#' . $details['line']; |
| 55 | + } else { |
| 56 | + $link = 'https://github.com/woocommerce/woocommerce/search?utf8=%E2%9C%93&q=' . $hook; |
| 57 | + } |
| 58 | + |
| 59 | + return '<a href="' . $link . '">' . $hook . '</a>'; |
| 60 | + } |
| 61 | + |
| 62 | + public static function process_hooks() { |
| 63 | + self::$files_to_scan = array(); |
| 64 | + |
| 65 | + self::$files_to_scan['Template Files'] = self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'templates/' ); |
| 66 | + self::$files_to_scan['Template Functions'] = array( self::SOURCE_PATH . 'includes/wc-template-functions.php', self::SOURCE_PATH . 'includes/wc-template-hooks.php' ); |
| 67 | + self::$files_to_scan['Shortcodes'] = self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/shortcodes/' ); |
| 68 | + self::$files_to_scan['Widgets'] = self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/widgets/' ); |
| 69 | + self::$files_to_scan['Data Stores'] = self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/data-stores' ); |
| 70 | + self::$files_to_scan['Core Classes'] = array_merge( |
| 71 | + self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/' ), |
| 72 | + self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/abstracts/' ), |
| 73 | + self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/customizer/' ), |
| 74 | + self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/emails/' ), |
| 75 | + self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/export/' ), |
| 76 | + self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/gateways/' ), |
| 77 | + self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/import/' ), |
| 78 | + self::get_files( '*.php', GLOB_MARK, self::SOURCE_PATH . 'includes/shipping/' ) |
| 79 | + ); |
| 80 | + |
| 81 | + self::$files_to_scan = array_filter( self::$files_to_scan ); |
| 82 | + |
| 83 | + $scanned = array(); |
| 84 | + |
| 85 | + ob_start(); |
| 86 | + |
| 87 | + $index = array(); |
| 88 | + |
| 89 | + foreach ( self::$files_to_scan as $heading => $files ) { |
| 90 | + $index[] = '<a href="#hooks-' . str_replace( ' ', '-', strtolower( $heading ) ) . '">' . $heading . '</a>'; |
| 91 | + } |
| 92 | + |
| 93 | + echo '<div id="content">'; |
| 94 | + echo '<h1>Action and Filter Hook Reference</h1>'; |
| 95 | + echo '<div class="description"> |
| 96 | + <p>This is simply a list of action and filter hooks found within WooCommerce files. View the source to see supported params and usage.</p> |
| 97 | + <p>' . implode( ', ', $index ) . '</p> |
| 98 | + </div>'; |
| 99 | + |
| 100 | + foreach ( self::$files_to_scan as $heading => $files ) { |
| 101 | + self::$custom_hooks_found = array(); |
| 102 | + |
| 103 | + foreach ( $files as $f ) { |
| 104 | + self::$current_file = basename( $f ); |
| 105 | + $tokens = token_get_all( file_get_contents( $f ) ); |
| 106 | + $token_type = false; |
| 107 | + $current_class = ''; |
| 108 | + $current_function = ''; |
| 109 | + |
| 110 | + if ( in_array( self::$current_file, $scanned ) ) { |
| 111 | + continue; |
| 112 | + } |
| 113 | + |
| 114 | + $scanned[] = self::$current_file; |
| 115 | + |
| 116 | + foreach ( $tokens as $index => $token ) { |
| 117 | + if ( is_array( $token ) ) { |
| 118 | + $trimmed_token_1 = trim( $token[1] ); |
| 119 | + if ( T_CLASS == $token[0] ) { |
| 120 | + $token_type = 'class'; |
| 121 | + } elseif ( T_FUNCTION == $token[0] ) { |
| 122 | + $token_type = 'function'; |
| 123 | + } elseif ( 'do_action' === $token[1] ) { |
| 124 | + $token_type = 'action'; |
| 125 | + } elseif ( 'apply_filters' === $token[1] ) { |
| 126 | + $token_type = 'filter'; |
| 127 | + } elseif ( $token_type && ! empty( $trimmed_token_1 ) ) { |
| 128 | + switch ( $token_type ) { |
| 129 | + case 'class' : |
| 130 | + $current_class = $token[1]; |
| 131 | + break; |
| 132 | + case 'function' : |
| 133 | + $current_function = $token[1]; |
| 134 | + break; |
| 135 | + case 'filter' : |
| 136 | + case 'action' : |
| 137 | + $hook = trim( $token[1], "'" ); |
| 138 | + $hook = str_replace( '_FUNCTION_', strtoupper( $current_function ), $hook ); |
| 139 | + $hook = str_replace( '_CLASS_', strtoupper( $current_class ), $hook ); |
| 140 | + $hook = str_replace( '$this', strtoupper( $current_class ), $hook ); |
| 141 | + $hook = str_replace( array( '.', '{', '}', '"', "'", ' ', ')', '(' ), '', $hook ); |
| 142 | + $loop = 0; |
| 143 | + |
| 144 | + // Keep adding to hook until we find a comma or colon |
| 145 | + while ( 1 ) { |
| 146 | + $loop ++; |
| 147 | + $prev_hook = is_string( $tokens[ $index + $loop - 1 ] ) ? $tokens[ $index + $loop - 1 ] : $tokens[ $index + $loop - 1 ][1]; |
| 148 | + $next_hook = is_string( $tokens[ $index + $loop ] ) ? $tokens[ $index + $loop ] : $tokens[ $index + $loop ][1]; |
| 149 | + |
| 150 | + if ( in_array( $next_hook, array( '.', '{', '}', '"', "'", ' ', ')', '(' ) ) ) { |
| 151 | + continue; |
| 152 | + } |
| 153 | + |
| 154 | + if ( in_array( $next_hook, array( ',', ';' ) ) ) { |
| 155 | + break; |
| 156 | + } |
| 157 | + |
| 158 | + $hook_first = substr( $next_hook, 0, 1 ); |
| 159 | + $hook_last = substr( $next_hook, -1, 1 ); |
| 160 | + |
| 161 | + if ( '{' === $hook_first || '}' === $hook_last || '$' === $hook_first || ')' === $hook_last || '>' === substr( $prev_hook, -1, 1 ) ) { |
| 162 | + $next_hook = strtoupper( $next_hook ); |
| 163 | + } |
| 164 | + |
| 165 | + $next_hook = str_replace( array( '.', '{', '}', '"', "'", ' ', ')', '(' ), '', $next_hook ); |
| 166 | + |
| 167 | + $hook .= $next_hook; |
| 168 | + } |
| 169 | + |
| 170 | + if ( isset( self::$custom_hooks_found[ $hook ] ) ) { |
| 171 | + self::$custom_hooks_found[ $hook ]['file'][] = self::$current_file; |
| 172 | + } else { |
| 173 | + self::$custom_hooks_found[ $hook ] = array( |
| 174 | + 'line' => $token[2], |
| 175 | + 'class' => $current_class, |
| 176 | + 'function' => $current_function, |
| 177 | + 'file' => array( self::$current_file ), |
| 178 | + 'type' => $token_type, |
| 179 | + ); |
| 180 | + } |
| 181 | + break; |
| 182 | + } |
| 183 | + $token_type = false; |
| 184 | + } |
| 185 | + } |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + foreach ( self::$custom_hooks_found as $hook => $details ) { |
| 190 | + if ( ! strstr( $hook, 'woocommerce' ) && ! strstr( $hook, 'product' ) && ! strstr( $hook, 'wc_' ) ) { |
| 191 | + //unset( self::$custom_hooks_found[ $hook ] ); |
| 192 | + } |
| 193 | + } |
| 194 | + |
| 195 | + ksort( self::$custom_hooks_found ); |
| 196 | + |
| 197 | + if ( ! empty( self::$custom_hooks_found ) ) { |
| 198 | + echo '<div class="panel panel-default"><div class="panel-heading"><h2 id="hooks-' . str_replace( ' ', '-', strtolower( $heading ) ) . '">' . $heading . '</h2></div>'; |
| 199 | + |
| 200 | + echo '<table class="summary table table-bordered table-striped"><thead><tr><th>Hook</th><th>Type</th><th>File(s)</th></tr></thead><tbody>'; |
| 201 | + |
| 202 | + foreach ( self::$custom_hooks_found as $hook => $details ) { |
| 203 | + echo '<tr> |
| 204 | + <td>' . self::get_hook_link( $hook, $details ) . '</td> |
| 205 | + <td>' . $details['type'] . '</td> |
| 206 | + <td>' . implode( ', ', array_unique( $details['file'] ) ) . '</td> |
| 207 | + </tr>' . "\n"; |
| 208 | + } |
| 209 | + |
| 210 | + echo '</tbody></table></div>'; |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + echo '</div><div id="footer">'; |
| 215 | + |
| 216 | + $html = file_get_contents( '../build/apidocs/tree.html' ); |
| 217 | + $header = explode( '<div id="content">', $html ); |
| 218 | + $header = str_replace( '<li class="active">', '<li>', current( $header ) ); |
| 219 | + $header = str_replace( '<li class="hooks">', '<li class="active">', $header ); |
| 220 | + $header = str_replace( 'Tree | ', 'Hook Reference | ', $header ); |
| 221 | + $footer = explode( '<div id="footer">', $html ); |
| 222 | + |
| 223 | + file_put_contents( '../build/apidocs/hook-docs.html', $header . ob_get_clean() . end( $footer ) ); |
| 224 | + echo "Hook docs generated :)\n"; |
| 225 | + } |
| 226 | +} |
| 227 | + |
| 228 | +WC_HookFinder::process_hooks(); |
0 commit comments