[ Index ] |
PHP Cross Reference of phpBB-3.1.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 namespace phpbb\event; 15 16 /** 17 * Class php_exporter 18 * Crawls through a list of files and grabs all php-events 19 */ 20 class php_exporter 21 { 22 /** @var string Path where we look for files*/ 23 protected $path; 24 25 /** @var string phpBB Root Path */ 26 protected $root_path; 27 28 /** @var string The minimum version for the events to return */ 29 protected $min_version; 30 31 /** @var string The maximum version for the events to return */ 32 protected $max_version; 33 34 /** @var string */ 35 protected $current_file; 36 37 /** @var string */ 38 protected $current_event; 39 40 /** @var int */ 41 protected $current_event_line; 42 43 /** @var array */ 44 protected $events; 45 46 /** @var array */ 47 protected $file_lines; 48 49 /** 50 * @param string $phpbb_root_path 51 * @param mixed $extension String 'vendor/ext' to filter, null for phpBB core 52 * @param string $min_version 53 * @param string $max_version 54 */ 55 public function __construct($phpbb_root_path, $extension = null, $min_version = null, $max_version = null) 56 { 57 $this->root_path = $phpbb_root_path; 58 $this->path = $phpbb_root_path; 59 $this->events = $this->file_lines = array(); 60 $this->current_file = $this->current_event = ''; 61 $this->current_event_line = 0; 62 $this->min_version = $min_version; 63 $this->max_version = $max_version; 64 65 $this->path = $this->root_path; 66 if ($extension) 67 { 68 $this->path .= 'ext/' . $extension . '/'; 69 } 70 } 71 72 /** 73 * Get the list of all events 74 * 75 * @return array Array with events: name => details 76 */ 77 public function get_events() 78 { 79 return $this->events; 80 } 81 82 /** 83 * Set current event data 84 * 85 * @param string $name Name of the current event (used for error messages) 86 * @param int $line Line where the current event is placed in 87 * @return null 88 */ 89 public function set_current_event($name, $line) 90 { 91 $this->current_event = $name; 92 $this->current_event_line = $line; 93 } 94 95 /** 96 * Set the content of this file 97 * 98 * @param array $content Array with the lines of the file 99 * @return null 100 */ 101 public function set_content($content) 102 { 103 $this->file_lines = $content; 104 } 105 106 /** 107 * Crawl the phpBB/ directory for php events 108 * @return int The number of events found 109 */ 110 public function crawl_phpbb_directory_php() 111 { 112 $files = $this->get_recursive_file_list(); 113 $this->events = array(); 114 foreach ($files as $file) 115 { 116 $this->crawl_php_file($file); 117 } 118 ksort($this->events); 119 120 return sizeof($this->events); 121 } 122 123 /** 124 * Returns a list of files in $dir 125 * 126 * @return array List of files (including the path) 127 */ 128 public function get_recursive_file_list() 129 { 130 try 131 { 132 $iterator = new \RecursiveIteratorIterator( 133 new \phpbb\event\recursive_event_filter_iterator( 134 new \RecursiveDirectoryIterator( 135 $this->path, 136 \FilesystemIterator::SKIP_DOTS 137 ), 138 $this->path 139 ), 140 \RecursiveIteratorIterator::LEAVES_ONLY 141 ); 142 } 143 catch (\Exception $e) 144 { 145 return array(); 146 } 147 148 $files = array(); 149 foreach ($iterator as $file_info) 150 { 151 /** @var \RecursiveDirectoryIterator $file_info */ 152 $relative_path = $iterator->getInnerIterator()->getSubPathname(); 153 $files[] = str_replace(DIRECTORY_SEPARATOR, '/', $relative_path); 154 } 155 156 return $files; 157 } 158 159 /** 160 * Format the php events as a wiki table 161 * 162 * @param string $action 163 * @return string 164 */ 165 public function export_events_for_wiki($action = '') 166 { 167 if ($action === 'diff') 168 { 169 $wiki_page = '=== PHP Events (Hook Locations) ===' . "\n"; 170 } 171 else 172 { 173 $wiki_page = '= PHP Events (Hook Locations) =' . "\n"; 174 } 175 $wiki_page .= '{| class="sortable zebra" cellspacing="0" cellpadding="5"' . "\n"; 176 $wiki_page .= '! Identifier !! Placement !! Arguments !! Added in Release !! Explanation' . "\n"; 177 foreach ($this->events as $event) 178 { 179 $wiki_page .= '|- id="' . $event['event'] . '"' . "\n"; 180 $wiki_page .= '| [[#' . $event['event'] . '|' . $event['event'] . ']] || ' . $event['file'] . ' || ' . implode(', ', $event['arguments']) . ' || ' . $event['since'] . ' || ' . $event['description'] . "\n"; 181 } 182 $wiki_page .= '|}' . "\n"; 183 184 return $wiki_page; 185 } 186 187 /** 188 * @param string $file 189 * @return int Number of events found in this file 190 * @throws \LogicException 191 */ 192 public function crawl_php_file($file) 193 { 194 $this->current_file = $file; 195 $this->file_lines = array(); 196 $content = file_get_contents($this->path . $this->current_file); 197 $num_events_found = 0; 198 199 if (strpos($content, "dispatcher->trigger_event('") || strpos($content, "dispatcher->dispatch('")) 200 { 201 $this->set_content(explode("\n", $content)); 202 for ($i = 0, $num_lines = sizeof($this->file_lines); $i < $num_lines; $i++) 203 { 204 $event_line = false; 205 $found_trigger_event = strpos($this->file_lines[$i], "dispatcher->trigger_event('"); 206 $arguments = array(); 207 if ($found_trigger_event !== false) 208 { 209 $event_line = $i; 210 $this->set_current_event($this->get_event_name($event_line, false), $event_line); 211 212 // Find variables of the event 213 $arguments = $this->get_vars_from_array(); 214 $doc_vars = $this->get_vars_from_docblock(); 215 $this->validate_vars_docblock_array($arguments, $doc_vars); 216 } 217 else 218 { 219 $found_dispatch = strpos($this->file_lines[$i], "dispatcher->dispatch('"); 220 if ($found_dispatch !== false) 221 { 222 $event_line = $i; 223 $this->set_current_event($this->get_event_name($event_line, true), $event_line); 224 } 225 } 226 227 if ($event_line) 228 { 229 // Validate @event 230 $event_line_num = $this->find_event(); 231 $this->validate_event($this->current_event, $this->file_lines[$event_line_num]); 232 233 // Validate @since 234 $since_line_num = $this->find_since(); 235 $since = $this->validate_since($this->file_lines[$since_line_num]); 236 237 $changed_line_nums = $this->find_changed('changed'); 238 if (empty($changed_line_nums)) 239 { 240 $changed_line_nums = $this->find_changed('change'); 241 } 242 $changed_versions = array(); 243 if (!empty($changed_line_nums)) 244 { 245 foreach ($changed_line_nums as $changed_line_num) 246 { 247 $changed_versions[] = $this->validate_changed($this->file_lines[$changed_line_num]); 248 } 249 } 250 251 if (!$this->version_is_filtered($since)) 252 { 253 $valid_version = false; 254 foreach ($changed_versions as $changed) 255 { 256 $valid_version = $valid_version || $this->version_is_filtered($changed); 257 } 258 259 if (!$valid_version) 260 { 261 continue; 262 } 263 } 264 265 // Find event description line 266 $description_line_num = $this->find_description(); 267 $description = substr(trim($this->file_lines[$description_line_num]), strlen('* ')); 268 269 if (isset($this->events[$this->current_event])) 270 { 271 throw new \LogicException("The event '{$this->current_event}' from file " 272 . "'{$this->current_file}:{$event_line_num}' already exists in file " 273 . "'{$this->events[$this->current_event]['file']}'", 10); 274 } 275 276 sort($arguments); 277 $this->events[$this->current_event] = array( 278 'event' => $this->current_event, 279 'file' => $this->current_file, 280 'arguments' => $arguments, 281 'since' => $since, 282 'description' => $description, 283 ); 284 $num_events_found++; 285 } 286 } 287 } 288 289 return $num_events_found; 290 } 291 292 /** 293 * The version to check 294 * 295 * @param string $version 296 * @return bool 297 */ 298 protected function version_is_filtered($version) 299 { 300 return (!$this->min_version || phpbb_version_compare($this->min_version, $version, '<=')) 301 && (!$this->max_version || phpbb_version_compare($this->max_version, $version, '>=')); 302 } 303 304 /** 305 * Find the name of the event inside the dispatch() line 306 * 307 * @param int $event_line 308 * @param bool $is_dispatch Do we look for dispatch() or trigger_event() ? 309 * @return string Name of the event 310 * @throws \LogicException 311 */ 312 public function get_event_name($event_line, $is_dispatch) 313 { 314 $event_text_line = $this->file_lines[$event_line]; 315 $event_text_line = ltrim($event_text_line, "\t "); 316 317 if ($is_dispatch) 318 { 319 $regex = '#\$([a-z](?:[a-z0-9_]|->)*)'; 320 $regex .= '->dispatch\('; 321 $regex .= '\'' . $this->preg_match_event_name() . '\''; 322 $regex .= '\);#'; 323 } 324 else 325 { 326 $regex = '#extract\(\$([a-z](?:[a-z0-9_]|->)*)'; 327 $regex .= '->trigger_event\('; 328 $regex .= '\'' . $this->preg_match_event_name() . '\''; 329 $regex .= ', compact\(\$vars\)\)\);#'; 330 } 331 332 $match = array(); 333 preg_match($regex, $event_text_line, $match); 334 if (!isset($match[2])) 335 { 336 throw new \LogicException("Can not find event name in line '{$event_text_line}' " 337 . "in file '{$this->current_file}:{$event_line}'", 1); 338 } 339 340 return $match[2]; 341 } 342 343 /** 344 * Returns a regex match for the event name 345 * 346 * @return string 347 */ 348 protected function preg_match_event_name() 349 { 350 return '([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+)'; 351 } 352 353 /** 354 * Find the $vars array 355 * 356 * @return array List of variables 357 * @throws \LogicException 358 */ 359 public function get_vars_from_array() 360 { 361 $line = ltrim($this->file_lines[$this->current_event_line - 1], "\t"); 362 if ($line === ');') 363 { 364 $vars_array = $this->get_vars_from_multi_line_array(); 365 } 366 else 367 { 368 $vars_array = $this->get_vars_from_single_line_array($line); 369 } 370 371 foreach ($vars_array as $var) 372 { 373 if (!preg_match('#^([a-zA-Z_][a-zA-Z0-9_]*)$#', $var)) 374 { 375 throw new \LogicException("Found invalid var '{$var}' in array for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 3); 376 } 377 } 378 379 sort($vars_array); 380 return $vars_array; 381 } 382 383 /** 384 * Find the variables in single line array 385 * 386 * @param string $line 387 * @param bool $throw_multiline Throw an exception when there are too 388 * many arguments in one line. 389 * @return array List of variables 390 * @throws \LogicException 391 */ 392 public function get_vars_from_single_line_array($line, $throw_multiline = true) 393 { 394 $match = array(); 395 preg_match('#^\$vars = array\(\'([a-zA-Z0-9_\' ,]+)\'\);$#', $line, $match); 396 397 if (isset($match[1])) 398 { 399 $vars_array = explode("', '", $match[1]); 400 if ($throw_multiline && sizeof($vars_array) > 6) 401 { 402 throw new \LogicException('Should use multiple lines for $vars definition ' 403 . "for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2); 404 } 405 return $vars_array; 406 } 407 else 408 { 409 throw new \LogicException("Can not find '\$vars = array();'-line for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1); 410 } 411 } 412 413 /** 414 * Find the variables in single line array 415 * 416 * @return array List of variables 417 * @throws \LogicException 418 */ 419 public function get_vars_from_multi_line_array() 420 { 421 $current_vars_line = 2; 422 $var_lines = array(); 423 while (ltrim($this->file_lines[$this->current_event_line - $current_vars_line], "\t") !== '$vars = array(') 424 { 425 $var_lines[] = substr(trim($this->file_lines[$this->current_event_line - $current_vars_line]), 0, -1); 426 427 $current_vars_line++; 428 if ($current_vars_line > $this->current_event_line) 429 { 430 // Reached the start of the file 431 throw new \LogicException("Can not find end of \$vars array for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2); 432 } 433 } 434 435 return $this->get_vars_from_single_line_array('$vars = array(' . implode(", ", $var_lines) . ');', false); 436 } 437 438 /** 439 * Find the $vars array 440 * 441 * @return array List of variables 442 * @throws \LogicException 443 */ 444 public function get_vars_from_docblock() 445 { 446 $doc_vars = array(); 447 $current_doc_line = 1; 448 $found_comment_end = false; 449 while (ltrim($this->file_lines[$this->current_event_line - $current_doc_line], "\t") !== '/**') 450 { 451 if (ltrim($this->file_lines[$this->current_event_line - $current_doc_line], "\t ") === '*/') 452 { 453 $found_comment_end = true; 454 } 455 456 if ($found_comment_end) 457 { 458 $var_line = trim($this->file_lines[$this->current_event_line - $current_doc_line]); 459 $var_line = preg_replace('!\s+!', ' ', $var_line); 460 if (strpos($var_line, '* @var ') === 0) 461 { 462 $doc_line = explode(' ', $var_line, 5); 463 if (sizeof($doc_line) !== 5) 464 { 465 throw new \LogicException("Found invalid line '{$this->file_lines[$this->current_event_line - $current_doc_line]}' " 466 . "for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1); 467 } 468 $doc_vars[] = $doc_line[3]; 469 } 470 } 471 472 $current_doc_line++; 473 if ($current_doc_line > $this->current_event_line) 474 { 475 // Reached the start of the file 476 throw new \LogicException("Can not find end of docblock for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2); 477 } 478 } 479 480 if (empty($doc_vars)) 481 { 482 // Reached the start of the file 483 throw new \LogicException("Can not find @var lines for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 3); 484 } 485 486 foreach ($doc_vars as $var) 487 { 488 if (!preg_match('#^([a-zA-Z_][a-zA-Z0-9_]*)$#', $var)) 489 { 490 throw new \LogicException("Found invalid @var '{$var}' in docblock for event " 491 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 4); 492 } 493 } 494 495 sort($doc_vars); 496 return $doc_vars; 497 } 498 499 /** 500 * Find the "@since" Information line 501 * 502 * @return int Absolute line number 503 * @throws \LogicException 504 */ 505 public function find_since() 506 { 507 return $this->find_tag('since', array('event', 'var')); 508 } 509 510 /** 511 * Find the "@changed" Information lines 512 * 513 * @param string $tag_name Should be 'change', not 'changed' 514 * @return array Absolute line numbers 515 * @throws \LogicException 516 */ 517 public function find_changed($tag_name) 518 { 519 $lines = array(); 520 $last_line = 0; 521 try 522 { 523 while ($line = $this->find_tag($tag_name, array('since'), $last_line)) 524 { 525 $lines[] = $line; 526 $last_line = $line; 527 } 528 } 529 catch (\LogicException $e) 530 { 531 // Not changed? No problem! 532 } 533 534 return $lines; 535 } 536 537 /** 538 * Find the "@event" Information line 539 * 540 * @return int Absolute line number 541 */ 542 public function find_event() 543 { 544 return $this->find_tag('event', array()); 545 } 546 547 /** 548 * Find a "@*" Information line 549 * 550 * @param string $find_tag Name of the tag we are trying to find 551 * @param array $disallowed_tags List of tags that must not appear between 552 * the tag and the actual event 553 * @param int $skip_to_line Skip lines until this one 554 * @return int Absolute line number 555 * @throws \LogicException 556 */ 557 public function find_tag($find_tag, $disallowed_tags, $skip_to_line = 0) 558 { 559 $find_tag_line = $skip_to_line ? $this->current_event_line - $skip_to_line + 1 : 0; 560 $found_comment_end = ($skip_to_line) ? true : false; 561 while (strpos(ltrim($this->file_lines[$this->current_event_line - $find_tag_line], "\t "), '* @' . $find_tag . ' ') !== 0) 562 { 563 if ($found_comment_end && ltrim($this->file_lines[$this->current_event_line - $find_tag_line], "\t") === '/**') 564 { 565 // Reached the start of this doc block 566 throw new \LogicException("Can not find '@{$find_tag}' information for event " 567 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1); 568 } 569 570 foreach ($disallowed_tags as $disallowed_tag) 571 { 572 if ($found_comment_end && strpos(ltrim($this->file_lines[$this->current_event_line - $find_tag_line], "\t "), '* @' . $disallowed_tag) === 0) 573 { 574 // Found @var after the @since 575 throw new \LogicException("Found '@{$disallowed_tag}' information after '@{$find_tag}' for event " 576 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 3); 577 } 578 } 579 580 if (ltrim($this->file_lines[$this->current_event_line - $find_tag_line], "\t ") === '*/') 581 { 582 $found_comment_end = true; 583 } 584 585 $find_tag_line++; 586 if ($find_tag_line >= $this->current_event_line) 587 { 588 // Reached the start of the file 589 throw new \LogicException("Can not find '@{$find_tag}' information for event " 590 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2); 591 } 592 } 593 594 return $this->current_event_line - $find_tag_line; 595 } 596 597 /** 598 * Find a "@*" Information line 599 * 600 * @return int Absolute line number 601 * @throws \LogicException 602 */ 603 public function find_description() 604 { 605 $find_desc_line = 0; 606 while (ltrim($this->file_lines[$this->current_event_line - $find_desc_line], "\t") !== '/**') 607 { 608 $find_desc_line++; 609 if ($find_desc_line > $this->current_event_line) 610 { 611 // Reached the start of the file 612 throw new \LogicException("Can not find a description for event " 613 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1); 614 } 615 } 616 617 $find_desc_line = $this->current_event_line - $find_desc_line + 1; 618 619 $desc = trim($this->file_lines[$find_desc_line]); 620 if (strpos($desc, '* @') === 0 || $desc[0] !== '*' || substr($desc, 1) == '') 621 { 622 // First line of the doc block is a @-line, empty or only contains "*" 623 throw new \LogicException("Can not find a description for event " 624 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2); 625 } 626 627 return $find_desc_line; 628 } 629 630 /** 631 * Validate "@since" Information 632 * 633 * @param string $line 634 * @return string 635 * @throws \LogicException 636 */ 637 public function validate_since($line) 638 { 639 $match = array(); 640 preg_match('#^\* @since (\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?)$#', ltrim($line, "\t "), $match); 641 if (!isset($match[1])) 642 { 643 throw new \LogicException("Invalid '@since' information for event " 644 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'"); 645 } 646 647 return $match[1]; 648 } 649 650 /** 651 * Validate "@changed" Information 652 * 653 * @param string $line 654 * @return string 655 * @throws \LogicException 656 */ 657 public function validate_changed($line) 658 { 659 $match = array(); 660 $line = str_replace("\t", ' ', ltrim($line, "\t ")); 661 preg_match('#^\* @changed (\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?)( (?:.*))?$#', $line, $match); 662 if (!isset($match[2])) 663 { 664 throw new \LogicException("Invalid '@changed' information for event " 665 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'"); 666 } 667 668 return $match[2]; 669 } 670 671 /** 672 * Validate "@event" Information 673 * 674 * @param string $event_name 675 * @param string $line 676 * @return string 677 * @throws \LogicException 678 */ 679 public function validate_event($event_name, $line) 680 { 681 $event = substr(ltrim($line, "\t "), strlen('* @event ')); 682 683 if ($event !== trim($event)) 684 { 685 throw new \LogicException("Invalid '@event' information for event " 686 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1); 687 } 688 689 if ($event !== $event_name) 690 { 691 throw new \LogicException("Event name does not match '@event' tag for event " 692 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2); 693 } 694 695 return $event; 696 } 697 698 /** 699 * Validates that two arrays contain the same strings 700 * 701 * @param array $vars_array Variables found in the array line 702 * @param array $vars_docblock Variables found in the doc block 703 * @return null 704 * @throws \LogicException 705 */ 706 public function validate_vars_docblock_array($vars_array, $vars_docblock) 707 { 708 $vars_array = array_unique($vars_array); 709 $vars_docblock = array_unique($vars_docblock); 710 $sizeof_vars_array = sizeof($vars_array); 711 712 if ($sizeof_vars_array !== sizeof($vars_docblock) || $sizeof_vars_array !== sizeof(array_intersect($vars_array, $vars_docblock))) 713 { 714 throw new \LogicException("\$vars array does not match the list of '@var' tags for event " 715 . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'"); 716 } 717 } 718 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |