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