Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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: 02.04.2025, 15:02 - Dateigröße: 16.52 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          // @codingStandardsIgnoreStart
264          while ($this->enable_step($name));
265          // @codingStandardsIgnoreEnd
266      }
267   
268      /**
269      * Disables an extension
270      *
271      * Calls the disable method on the extension's meta class to allow it to
272      * process the event.
273      *
274      * @param string $name The extension's name
275      * @return bool False if disabling is finished, true otherwise
276      */
277      public function disable_step($name)
278      {
279          // ignore extensions that are not enabled
280          if (!$this->is_enabled($name))
281          {
282              return false;
283          }
284   
285          $old_state = unserialize($this->extensions[$name]['ext_state']);
286   
287          $extension = $this->get_extension($name);
288          $state = $extension->disable_step($old_state);
289          $active = ($state !== false);
290   
291          $extension_data = array(
292              'ext_active'    => $active,
293              'ext_state'        => serialize($state),
294          );
295          $this->update_state($name, $extension_data);
296   
297          return $active;
298      }
299   
300      /**
301      * Disables an extension
302      *
303      * Disables an extension completely at once. This process could run for a
304      * while so never call this in a script that has a max_execution time.
305      *
306      * @param string $name The extension's name
307      * @return null
308      */
309      public function disable($name)
310      {
311          // @codingStandardsIgnoreStart
312          while ($this->disable_step($name));
313          // @codingStandardsIgnoreEnd
314      }
315   
316      /**
317      * Purge an extension
318      *
319      * Disables the extension first if active, and then calls purge on the
320      * extension's meta class to delete the extension's database content.
321      *
322      * @param string $name The extension's name
323      * @return bool False if purging is finished, true otherwise
324      */
325      public function purge_step($name)
326      {
327          // ignore extensions that are not configured
328          if (!$this->is_configured($name))
329          {
330              return false;
331          }
332   
333          // disable first if necessary
334          if ($this->extensions[$name]['ext_active'])
335          {
336              $this->disable($name);
337          }
338   
339          $old_state = unserialize($this->extensions[$name]['ext_state']);
340   
341          $extension = $this->get_extension($name);
342          $state = $extension->purge_step($old_state);
343          $purged = ($state === false);
344   
345          $extension_data = array(
346              'ext_state'    => serialize($state),
347          );
348   
349          $this->update_state($name, $extension_data, $purged ? 'delete' : 'update');
350   
351          // continue until the state is false
352          return !$purged;
353      }
354   
355      /**
356      * Purge an extension
357      *
358      * Purges an extension completely at once. This process could run for a while
359      * so never call this in a script that has a max_execution time.
360      *
361      * @param string $name The extension's name
362      * @return null
363      */
364      public function purge($name)
365      {
366          // @codingStandardsIgnoreStart
367          while ($this->purge_step($name));
368          // @codingStandardsIgnoreEnd
369      }
370   
371      /**
372      * Retrieves a list of all available extensions on the filesystem
373      *
374      * @return array An array with extension names as keys and paths to the
375      *               extension as values
376      */
377      public function all_available()
378      {
379          $available = array();
380          if (!is_dir($this->phpbb_root_path . 'ext/'))
381          {
382              return $available;
383          }
384   
385          $iterator = new \RecursiveIteratorIterator(
386              new \phpbb\recursive_dot_prefix_filter_iterator(
387                  new \RecursiveDirectoryIterator($this->phpbb_root_path . 'ext/', \FilesystemIterator::NEW_CURRENT_AND_KEY | \FilesystemIterator::FOLLOW_SYMLINKS)
388              ),
389              \RecursiveIteratorIterator::SELF_FIRST
390          );
391          $iterator->setMaxDepth(2);
392   
393          foreach ($iterator as $file_info)
394          {
395              if ($file_info->isFile() && $file_info->getFilename() == 'composer.json')
396              {
397                  $ext_name = $iterator->getInnerIterator()->getSubPath();
398                  $ext_name = str_replace(DIRECTORY_SEPARATOR, '/', $ext_name);
399                  if ($this->is_available($ext_name))
400                  {
401                      $available[$ext_name] = $this->get_extension_path($ext_name, true);
402                  }
403              }
404          }
405          ksort($available);
406          return $available;
407      }
408   
409      /**
410      * Retrieves all configured extensions.
411      *
412      * All enabled and disabled extensions are considered configured. A purged
413      * extension that is no longer in the database is not configured.
414      *
415      * @param bool $phpbb_relative Whether the path should be relative to phpbb root
416      *
417      * @return array An array with extension names as keys and and the
418      *               database stored extension information as values
419      */
420      public function all_configured($phpbb_relative = true)
421      {
422          $configured = array();
423          foreach ($this->extensions as $name => $data)
424          {
425              if ($this->is_configured($name))
426              {
427                  unset($data['metadata']);
428                  $data['ext_path'] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
429                  $configured[$name] = $data;
430              }
431          }
432          return $configured;
433      }
434   
435      /**
436      * Retrieves all enabled extensions.
437      * @param bool $phpbb_relative Whether the path should be relative to phpbb root
438      *
439      * @return array An array with extension names as keys and and the
440      *               database stored extension information as values
441      */
442      public function all_enabled($phpbb_relative = true)
443      {
444          $enabled = array();
445          foreach ($this->extensions as $name => $data)
446          {
447              if ($this->is_enabled($name))
448              {
449                  $enabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
450              }
451          }
452          return $enabled;
453      }
454   
455      /**
456      * Retrieves all disabled extensions.
457      *
458      * @param bool $phpbb_relative Whether the path should be relative to phpbb root
459      *
460      * @return array An array with extension names as keys and and the
461      *               database stored extension information as values
462      */
463      public function all_disabled($phpbb_relative = true)
464      {
465          $disabled = array();
466          foreach ($this->extensions as $name => $data)
467          {
468              if ($this->is_disabled($name))
469              {
470                  $disabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
471              }
472          }
473          return $disabled;
474      }
475   
476      /**
477      * Check to see if a given extension is available on the filesystem
478      *
479      * @param string $name Extension name to check NOTE: Can be user input
480      * @return bool Depending on whether or not the extension is available
481      */
482      public function is_available($name)
483      {
484          $md_manager = $this->create_extension_metadata_manager($name);
485          try
486          {
487              return $md_manager->get_metadata('all') && $md_manager->validate_enable();
488          }
489          catch (\phpbb\extension\exception $e)
490          {
491              return false;
492          }
493      }
494   
495      /**
496      * Check to see if a given extension is enabled
497      *
498      * @param string $name Extension name to check
499      * @return bool Depending on whether or not the extension is enabled
500      */
501      public function is_enabled($name)
502      {
503          return isset($this->extensions[$name]['ext_active']) && $this->extensions[$name]['ext_active'];
504      }
505   
506      /**
507      * Check to see if a given extension is disabled
508      *
509      * @param string $name Extension name to check
510      * @return bool Depending on whether or not the extension is disabled
511      */
512      public function is_disabled($name)
513      {
514          return isset($this->extensions[$name]['ext_active']) && !$this->extensions[$name]['ext_active'];
515      }
516   
517      /**
518      * Check to see if a given extension is configured
519      *
520      * All enabled and disabled extensions are considered configured. A purged
521      * extension that is no longer in the database is not configured.
522      *
523      * @param string $name Extension name to check
524      * @return bool Depending on whether or not the extension is configured
525      */
526      public function is_configured($name)
527      {
528          return isset($this->extensions[$name]['ext_active']);
529      }
530   
531      /**
532      * Check the version and return the available updates (for an extension).
533      *
534      * @param \phpbb\extension\metadata_manager $md_manager The metadata manager for the version to check.
535      * @param bool $force_update Ignores cached data. Defaults to false.
536      * @param bool $force_cache Force the use of the cache. Override $force_update.
537      * @param string $stability Force the stability (null by default).
538      * @return array
539      * @throws runtime_exception
540      */
541      public function version_check(\phpbb\extension\metadata_manager $md_manager, $force_update = false, $force_cache = false, $stability = null)
542      {
543          $meta = $md_manager->get_metadata('all');
544   
545          if (!isset($meta['extra']['version-check']))
546          {
547              throw new runtime_exception('NO_VERSIONCHECK');
548          }
549   
550          $version_check = $meta['extra']['version-check'];
551   
552          $version_helper = new \phpbb\version_helper($this->cache, $this->config, new file_downloader());
553          $version_helper->set_current_version($meta['version']);
554          $version_helper->set_file_location($version_check['host'], $version_check['directory'], $version_check['filename'], isset($version_check['ssl']) ? $version_check['ssl'] : false);
555          $version_helper->force_stability($stability);
556   
557          return $version_helper->get_ext_update_on_branch($force_update, $force_cache);
558      }
559   
560      /**
561      * Check to see if a given extension is purged
562      *
563      * An extension is purged if it is available, not enabled and not disabled.
564      *
565      * @param string $name Extension name to check
566      * @return bool Depending on whether or not the extension is purged
567      */
568      public function is_purged($name)
569      {
570          return $this->is_available($name) && !$this->is_configured($name);
571      }
572   
573      /**
574      * Instantiates a \phpbb\finder.
575      *
576      * @param bool $use_all_available Should we load all extensions, or just enabled ones
577      * @return \phpbb\finder An extension finder instance
578      */
579      public function get_finder($use_all_available = false)
580      {
581          $finder = new \phpbb\finder($this->filesystem, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder');
582          if ($use_all_available)
583          {
584              $finder->set_extensions(array_keys($this->all_available()));
585          }
586          else
587          {
588              $finder->set_extensions(array_keys($this->all_enabled()));
589          }
590          return $finder;
591      }
592  }
593