Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

php_exporter.php

Zuletzt modifiziert: 09.10.2024, 12:52 - Dateigröße: 19.42 KiB


001  <?php
002  /**
003  *
004  * This file is part of the phpBB Forum Software package.
005  *
006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
007  * @license GNU General Public License, version 2 (GPL-2.0)
008  *
009  * For full copyright and license information, please see
010  * the docs/CREDITS.txt file.
011  *
012  */
013   
014  namespace phpbb\event;
015   
016  /**
017  * Class php_exporter
018  * Crawls through a list of files and grabs all php-events
019  */
020  class php_exporter
021  {
022      /** @var string Path where we look for files*/
023      protected $path;
024   
025      /** @var string phpBB Root Path */
026      protected $root_path;
027   
028      /** @var string The minimum version for the events to return */
029      protected $min_version;
030   
031      /** @var string The maximum version for the events to return */
032      protected $max_version;
033   
034      /** @var string */
035      protected $current_file;
036   
037      /** @var string */
038      protected $current_event;
039   
040      /** @var int */
041      protected $current_event_line;
042   
043      /** @var array */
044      protected $events;
045   
046      /** @var array */
047      protected $file_lines;
048   
049      /**
050      * @param string $phpbb_root_path
051      * @param mixed $extension    String 'vendor/ext' to filter, null for phpBB core
052      * @param string $min_version
053      * @param string $max_version
054      */
055      public function __construct($phpbb_root_path, $extension = null, $min_version = null, $max_version = null)
056      {
057          $this->root_path = $phpbb_root_path;
058          $this->path = $phpbb_root_path;
059          $this->events = $this->file_lines = array();
060          $this->current_file = $this->current_event = '';
061          $this->current_event_line = 0;
062          $this->min_version = $min_version;
063          $this->max_version = $max_version;
064   
065          $this->path = $this->root_path;
066          if ($extension)
067          {
068              $this->path .= 'ext/' . $extension . '/';
069          }
070      }
071   
072      /**
073      * Get the list of all events
074      *
075      * @return array        Array with events: name => details
076      */
077      public function get_events()
078      {
079          return $this->events;
080      }
081   
082      /**
083      * Set current event data
084      *
085      * @param string    $name    Name of the current event (used for error messages)
086      * @param int    $line    Line where the current event is placed in
087      * @return null
088      */
089      public function set_current_event($name, $line)
090      {
091          $this->current_event = $name;
092          $this->current_event_line = $line;
093      }
094   
095      /**
096      * Set the content of this file
097      *
098      * @param array $content        Array with the lines of the file
099      * @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 'changed' or 'change'
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('#^\* @change(d)? (\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  }
719