Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

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

manager.php

Zuletzt modifiziert: 01.05.2026, 11:25 - Dateigröße: 16.33 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\extension;
015   
016  use phpbb\exception\runtime_exception;
017  use phpbb\file_downloader;
018  use Symfony\Component\DependencyInjection\ContainerInterface;
019   
020  /**
021  * The extension manager provides means to activate/deactivate extensions.
022  */
023  class manager
024  {
025      /** @var ContainerInterface */
026      protected $container;
027   
028      protected $db;
029      protected $config;
030      protected $cache;
031      protected $php_ext;
032      protected $extensions;
033      protected $extension_table;
034      protected $filesystem;
035      protected $phpbb_root_path;
036      protected $cache_name;
037   
038      /**
039      * Creates a manager and loads information from database
040      *
041      * @param ContainerInterface $container A container
042      * @param \phpbb\db\driver\driver_interface $db A database connection
043      * @param \phpbb\config\config $config Config object
044      * @param \phpbb\filesystem\filesystem_interface $filesystem
045      * @param string $extension_table The name of the table holding extensions
046      * @param string $phpbb_root_path Path to the phpbb includes directory.
047      * @param string $php_ext php file extension, defaults to php
048      * @param \phpbb\cache\service $cache A cache instance or null
049      * @param string $cache_name The name of the cache variable, defaults to _ext
050      */
051      public function __construct(ContainerInterface $container, \phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\filesystem\filesystem_interface $filesystem, $extension_table, $phpbb_root_path, $php_ext = 'php', \phpbb\cache\service $cache = null, $cache_name = '_ext')
052      {
053          $this->cache = $cache;
054          $this->cache_name = $cache_name;
055          $this->config = $config;
056          $this->container = $container;
057          $this->db = $db;
058          $this->extension_table = $extension_table;
059          $this->filesystem = $filesystem;
060          $this->phpbb_root_path = $phpbb_root_path;
061          $this->php_ext = $php_ext;
062   
063          $this->extensions = ($this->cache) ? $this->cache->get($this->cache_name) : false;
064   
065          if ($this->extensions === false)
066          {
067              $this->load_extensions();
068          }
069      }
070   
071      /**
072      * Loads all extension information from the database
073      *
074      * @return null
075      */
076      public function load_extensions()
077      {
078          $this->extensions = array();
079   
080          // Do not try to load any extensions if the extension table
081          // does not exist or when installing or updating.
082          // Note: database updater invokes this code, and in 3.0
083          // there is no extension table therefore the rest of this function
084          // fails
085          if (defined('IN_INSTALL') || version_compare($this->config['version'], '3.1.0-dev', '<'))
086          {
087              return;
088          }
089   
090          $sql = 'SELECT *
091              FROM ' . $this->extension_table;
092   
093          $result = $this->db->sql_query($sql);
094          $extensions = $this->db->sql_fetchrowset($result);
095          $this->db->sql_freeresult($result);
096   
097          foreach ($extensions as $extension)
098          {
099              $extension['ext_path'] = $this->get_extension_path($extension['ext_name']);
100              $this->extensions[$extension['ext_name']] = $extension;
101          }
102   
103          ksort($this->extensions);
104   
105          if ($this->cache)
106          {
107              $this->cache->put($this->cache_name, $this->extensions);
108          }
109      }
110   
111      /**
112      * Generates the path to an extension
113      *
114      * @param string $name The name of the extension
115      * @param bool $phpbb_relative Whether the path should be relative to phpbb root
116      * @return string Path to an extension
117      */
118      public function get_extension_path($name, $phpbb_relative = false)
119      {
120          $name = str_replace('.', '', $name);
121   
122          return (($phpbb_relative) ? $this->phpbb_root_path : '') . 'ext/' . $name . '/';
123      }
124   
125      /**
126      * Instantiates the extension meta class for the extension with the given name
127      *
128      * @param string $name The extension name
129      * @return \phpbb\extension\extension_interface Instance of the extension meta class or
130      *                     \phpbb\extension\base if the class does not exist
131      */
132      public function get_extension($name)
133      {
134          $extension_class_name = str_replace('/', '\\', $name) . '\\ext';
135   
136          $migrator = $this->container->get('migrator');
137   
138          if (class_exists($extension_class_name))
139          {
140              return new $extension_class_name($this->container, $this->get_finder(), $migrator, $name, $this->get_extension_path($name, true));
141          }
142          else
143          {
144              return new \phpbb\extension\base($this->container, $this->get_finder(), $migrator, $name, $this->get_extension_path($name, true));
145          }
146      }
147   
148      /**
149      * Instantiates the metadata manager for the extension with the given name
150      *
151      * @param string $name The extension name
152      * @return \phpbb\extension\metadata_manager Instance of the metadata manager
153      */
154      public function create_extension_metadata_manager($name)
155      {
156          if (!isset($this->extensions[$name]['metadata']))
157          {
158              $metadata = new \phpbb\extension\metadata_manager($name, $this->get_extension_path($name, true));
159              $this->extensions[$name]['metadata'] = $metadata;
160          }
161          return $this->extensions[$name]['metadata'];
162      }
163   
164      /**
165      * Update the database entry for an extension
166      *
167      * @param string $name Extension name to update
168      * @param array    $data Data to update in the database
169      * @param string    $action Action to perform, by default 'update', may be also 'insert' or 'delete'
170      */
171      protected function update_state($name, $data, $action = 'update')
172      {
173          switch ($action)
174          {
175              case 'insert':
176                  $this->extensions[$name] = $data;
177                  $this->extensions[$name]['ext_path'] = $this->get_extension_path($name);
178                  ksort($this->extensions);
179                  $sql = 'INSERT INTO ' . $this->extension_table . ' ' . $this->db->sql_build_array('INSERT', $data);
180                  $this->db->sql_query($sql);
181              break;
182   
183              case 'update':
184                  $this->extensions[$name] = array_merge($this->extensions[$name], $data);
185                  $sql = 'UPDATE ' . $this->extension_table . '
186                      SET ' . $this->db->sql_build_array('UPDATE', $data) . "
187                      WHERE ext_name = '" . $this->db->sql_escape($name) . "'";
188                  $this->db->sql_query($sql);
189              break;
190   
191              case 'delete':
192                  unset($this->extensions[$name]);
193                  $sql = 'DELETE FROM ' . $this->extension_table . "
194                      WHERE ext_name = '" . $this->db->sql_escape($name) . "'";
195                  $this->db->sql_query($sql);
196              break;
197          }
198   
199          if ($this->cache)
200          {
201              $this->cache->deferred_purge();
202          }
203      }
204   
205      /**
206      * Runs a step of the extension enabling process.
207      *
208      * Allows the exentension to enable in a long running script that works
209      * in multiple steps across requests. State is kept for the extension
210      * in the extensions table.
211      *
212      * @param    string    $name    The extension's name
213      * @return    bool            False if enabling is finished, true otherwise
214      */
215      public function enable_step($name)
216      {
217          // ignore extensions that are already enabled
218          if ($this->is_enabled($name))
219          {
220              return false;
221          }
222   
223          $old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false;
224   
225          $extension = $this->get_extension($name);
226   
227          if (!$extension->is_enableable())
228          {
229              return false;
230          }
231   
232          $state = $extension->enable_step($old_state);
233   
234          $active = ($state === false);
235   
236          $extension_data = array(
237              'ext_name'        => $name,
238              'ext_active'    => $active,
239              'ext_state'        => serialize($state),
240          );
241   
242          $this->update_state($name, $extension_data, $this->is_configured($name) ? 'update' : 'insert');
243   
244          if ($active)
245          {
246              $this->config->increment('assets_version', 1);
247          }
248   
249          return !$active;
250      }
251   
252      /**
253      * Enables an extension
254      *
255      * This method completely enables an extension. But it could be long running
256      * so never call this in a script that has a max_execution time.
257      *
258      * @param string $name The extension's name
259      * @return null
260      */
261      public function enable($name)
262      {
263          while ($this->enable_step($name));
264      }
265   
266      /**
267      * Disables an extension
268      *
269      * Calls the disable method on the extension's meta class to allow it to
270      * process the event.
271      *
272      * @param string $name The extension's name
273      * @return bool False if disabling is finished, true otherwise
274      */
275      public function disable_step($name)
276      {
277          // ignore extensions that are not enabled
278          if (!$this->is_enabled($name))
279          {
280              return false;
281          }
282   
283          $old_state = unserialize($this->extensions[$name]['ext_state']);
284   
285          $extension = $this->get_extension($name);
286          $state = $extension->disable_step($old_state);
287          $active = ($state !== false);
288   
289          $extension_data = array(
290              'ext_active'    => $active,
291              'ext_state'        => serialize($state),
292          );
293          $this->update_state($name, $extension_data);
294   
295          return $active;
296      }
297   
298      /**
299      * Disables an extension
300      *
301      * Disables an extension completely at once. This process could run for a
302      * while so never call this in a script that has a max_execution time.
303      *
304      * @param string $name The extension's name
305      * @return null
306      */
307      public function disable($name)
308      {
309          while ($this->disable_step($name));
310      }
311   
312      /**
313      * Purge an extension
314      *
315      * Disables the extension first if active, and then calls purge on the
316      * extension's meta class to delete the extension's database content.
317      *
318      * @param string $name The extension's name
319      * @return bool False if purging is finished, true otherwise
320      */
321      public function purge_step($name)
322      {
323          // ignore extensions that are not configured
324          if (!$this->is_configured($name))
325          {
326              return false;
327          }
328   
329          // disable first if necessary
330          if ($this->extensions[$name]['ext_active'])
331          {
332              $this->disable($name);
333          }
334   
335          $old_state = unserialize($this->extensions[$name]['ext_state']);
336   
337          $extension = $this->get_extension($name);
338          $state = $extension->purge_step($old_state);
339          $purged = ($state === false);
340   
341          $extension_data = array(
342              'ext_state'    => serialize($state),
343          );
344   
345          $this->update_state($name, $extension_data, $purged ? 'delete' : 'update');
346   
347          // continue until the state is false
348          return !$purged;
349      }
350   
351      /**
352      * Purge an extension
353      *
354      * Purges an extension completely at once. This process could run for a while
355      * so never call this in a script that has a max_execution time.
356      *
357      * @param string $name The extension's name
358      * @return null
359      */
360      public function purge($name)
361      {
362          while ($this->purge_step($name));
363      }
364   
365      /**
366      * Retrieves a list of all available extensions on the filesystem
367      *
368      * @return array An array with extension names as keys and paths to the
369      *               extension as values
370      */
371      public function all_available()
372      {
373          $available = array();
374          if (!is_dir($this->phpbb_root_path . 'ext/'))
375          {
376              return $available;
377          }
378   
379          $iterator = new \RecursiveIteratorIterator(
380              new \phpbb\recursive_dot_prefix_filter_iterator(
381                  new \RecursiveDirectoryIterator($this->phpbb_root_path . 'ext/', \FilesystemIterator::NEW_CURRENT_AND_KEY | \FilesystemIterator::FOLLOW_SYMLINKS)
382              ),
383              \RecursiveIteratorIterator::SELF_FIRST
384          );
385          $iterator->setMaxDepth(2);
386   
387          foreach ($iterator as $file_info)
388          {
389              if ($file_info->isFile() && $file_info->getFilename() == 'composer.json')
390              {
391                  $ext_name = $iterator->getInnerIterator()->getSubPath();
392                  $ext_name = str_replace(DIRECTORY_SEPARATOR, '/', $ext_name);
393                  if ($this->is_available($ext_name))
394                  {
395                      $available[$ext_name] = $this->get_extension_path($ext_name, true);
396                  }
397              }
398          }
399          ksort($available);
400          return $available;
401      }
402   
403      /**
404      * Retrieves all configured extensions.
405      *
406      * All enabled and disabled extensions are considered configured. A purged
407      * extension that is no longer in the database is not configured.
408      *
409      * @param bool $phpbb_relative Whether the path should be relative to phpbb root
410      *
411      * @return array An array with extension names as keys and and the
412      *               database stored extension information as values
413      */
414      public function all_configured($phpbb_relative = true)
415      {
416          $configured = array();
417          foreach ($this->extensions as $name => $data)
418          {
419              if ($this->is_configured($name))
420              {
421                  unset($data['metadata']);
422                  $data['ext_path'] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
423                  $configured[$name] = $data;
424              }
425          }
426          return $configured;
427      }
428   
429      /**
430      * Retrieves all enabled extensions.
431      * @param bool $phpbb_relative Whether the path should be relative to phpbb root
432      *
433      * @return array An array with extension names as keys and and the
434      *               database stored extension information as values
435      */
436      public function all_enabled($phpbb_relative = true)
437      {
438          $enabled = array();
439          foreach ($this->extensions as $name => $data)
440          {
441              if ($this->is_enabled($name))
442              {
443                  $enabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
444              }
445          }
446          return $enabled;
447      }
448   
449      /**
450      * Retrieves all disabled extensions.
451      *
452      * @param bool $phpbb_relative Whether the path should be relative to phpbb root
453      *
454      * @return array An array with extension names as keys and and the
455      *               database stored extension information as values
456      */
457      public function all_disabled($phpbb_relative = true)
458      {
459          $disabled = array();
460          foreach ($this->extensions as $name => $data)
461          {
462              if ($this->is_disabled($name))
463              {
464                  $disabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
465              }
466          }
467          return $disabled;
468      }
469   
470      /**
471      * Check to see if a given extension is available on the filesystem
472      *
473      * @param string $name Extension name to check NOTE: Can be user input
474      * @return bool Depending on whether or not the extension is available
475      */
476      public function is_available($name)
477      {
478          $md_manager = $this->create_extension_metadata_manager($name);
479          try
480          {
481              return $md_manager->get_metadata('all') && $md_manager->validate_enable();
482          }
483          catch (\phpbb\extension\exception $e)
484          {
485              return false;
486          }
487      }
488   
489      /**
490      * Check to see if a given extension is enabled
491      *
492      * @param string $name Extension name to check
493      * @return bool Depending on whether or not the extension is enabled
494      */
495      public function is_enabled($name)
496      {
497          return isset($this->extensions[$name]['ext_active']) && $this->extensions[$name]['ext_active'];
498      }
499   
500      /**
501      * Check to see if a given extension is disabled
502      *
503      * @param string $name Extension name to check
504      * @return bool Depending on whether or not the extension is disabled
505      */
506      public function is_disabled($name)
507      {
508          return isset($this->extensions[$name]['ext_active']) && !$this->extensions[$name]['ext_active'];
509      }
510   
511      /**
512      * Check to see if a given extension is configured
513      *
514      * All enabled and disabled extensions are considered configured. A purged
515      * extension that is no longer in the database is not configured.
516      *
517      * @param string $name Extension name to check
518      * @return bool Depending on whether or not the extension is configured
519      */
520      public function is_configured($name)
521      {
522          return isset($this->extensions[$name]['ext_active']);
523      }
524   
525      /**
526      * Check the version and return the available updates (for an extension).
527      *
528      * @param \phpbb\extension\metadata_manager $md_manager The metadata manager for the version to check.
529      * @param bool $force_update Ignores cached data. Defaults to false.
530      * @param bool $force_cache Force the use of the cache. Override $force_update.
531      * @param string $stability Force the stability (null by default).
532      * @return array
533      * @throws runtime_exception
534      */
535      public function version_check(\phpbb\extension\metadata_manager $md_manager, $force_update = false, $force_cache = false, $stability = null)
536      {
537          $meta = $md_manager->get_metadata('all');
538   
539          if (!isset($meta['extra']['version-check']))
540          {
541              throw new runtime_exception('NO_VERSIONCHECK');
542          }
543   
544          $version_check = $meta['extra']['version-check'];
545   
546          $version_helper = new \phpbb\version_helper($this->cache, $this->config, new file_downloader());
547          $version_helper->set_current_version($meta['version']);
548          $version_helper->set_file_location($version_check['host'], $version_check['directory'], $version_check['filename'], isset($version_check['ssl']) ? $version_check['ssl'] : false);
549          $version_helper->force_stability($stability);
550   
551          return $version_helper->get_ext_update_on_branch($force_update, $force_cache);
552      }
553   
554      /**
555      * Check to see if a given extension is purged
556      *
557      * An extension is purged if it is available, not enabled and not disabled.
558      *
559      * @param string $name Extension name to check
560      * @return bool Depending on whether or not the extension is purged
561      */
562      public function is_purged($name)
563      {
564          return $this->is_available($name) && !$this->is_configured($name);
565      }
566   
567      /**
568      * Instantiates a \phpbb\finder.
569      *
570      * @param bool $use_all_available Should we load all extensions, or just enabled ones
571      * @return \phpbb\finder An extension finder instance
572      */
573      public function get_finder($use_all_available = false)
574      {
575          $finder = new \phpbb\finder($this->filesystem, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder');
576          if ($use_all_available)
577          {
578              $finder->set_extensions(array_keys($this->all_available()));
579          }
580          else
581          {
582              $finder->set_extensions(array_keys($this->all_enabled()));
583          }
584          return $finder;
585      }
586  }
587