MediaWiki REL1_41
ExtensionProcessor.php
Go to the documentation of this file.
1<?php
2
5
12class ExtensionProcessor implements Processor {
13
19 protected static $globalSettings = [
20 MainConfigNames::ActionFilteredLogs,
21 MainConfigNames::Actions,
22 MainConfigNames::AddGroups,
23 MainConfigNames::APIFormatModules,
24 MainConfigNames::APIListModules,
25 MainConfigNames::APIMetaModules,
26 MainConfigNames::APIModules,
27 MainConfigNames::APIPropModules,
28 MainConfigNames::AuthManagerAutoConfig,
29 MainConfigNames::AvailableRights,
30 MainConfigNames::CentralIdLookupProviders,
31 MainConfigNames::ChangeCredentialsBlacklist,
32 MainConfigNames::ConfigRegistry,
33 MainConfigNames::ContentHandlers,
34 MainConfigNames::DefaultUserOptions,
35 MainConfigNames::ExtensionEntryPointListFiles,
36 MainConfigNames::ExtensionFunctions,
37 MainConfigNames::FeedClasses,
38 MainConfigNames::FileExtensions,
39 MainConfigNames::FilterLogTypes,
40 MainConfigNames::GrantPermissionGroups,
41 MainConfigNames::GrantPermissions,
42 MainConfigNames::GroupPermissions,
43 MainConfigNames::GroupsAddToSelf,
44 MainConfigNames::GroupsRemoveFromSelf,
45 MainConfigNames::HiddenPrefs,
46 MainConfigNames::ImplicitGroups,
47 MainConfigNames::JobClasses,
48 MainConfigNames::LogActions,
49 MainConfigNames::LogActionsHandlers,
50 MainConfigNames::LogHeaders,
51 MainConfigNames::LogNames,
52 MainConfigNames::LogRestrictions,
53 MainConfigNames::LogTypes,
54 MainConfigNames::MediaHandlers,
55 MainConfigNames::PasswordPolicy,
56 MainConfigNames::PrivilegedGroups,
57 MainConfigNames::RateLimits,
58 MainConfigNames::RawHtmlMessages,
59 MainConfigNames::ReauthenticateTime,
60 MainConfigNames::RecentChangesFlags,
61 MainConfigNames::RemoveCredentialsBlacklist,
62 MainConfigNames::RemoveGroups,
63 MainConfigNames::ResourceLoaderSources,
64 MainConfigNames::RevokePermissions,
65 MainConfigNames::SessionProviders,
66 MainConfigNames::SpecialPages,
67 MainConfigNames::UserRegistrationProviders,
68 ];
69
75 protected const CORE_ATTRIBS = [
76 'ParsoidModules',
77 'RestRoutes',
78 'SkinOOUIThemes',
79 'SkinCodexThemes',
80 'SearchMappings',
81 'TrackingCategories',
82 'LateJSConfigVarNames',
83 'TempUserSerialProviders',
84 'TempUserSerialMappings',
85 'DatabaseVirtualDomains',
86 ];
87
95 protected const MERGE_STRATEGIES = [
96 'wgAuthManagerAutoConfig' => 'array_plus_2d',
97 'wgCapitalLinkOverrides' => 'array_plus',
98 'wgExtraGenderNamespaces' => 'array_plus',
99 'wgGrantPermissions' => 'array_plus_2d',
100 'wgGroupPermissions' => 'array_plus_2d',
101 'wgHooks' => 'array_merge_recursive',
102 'wgNamespaceContentModels' => 'array_plus',
103 'wgNamespaceProtection' => 'array_plus',
104 'wgNamespacesWithSubpages' => 'array_plus',
105 'wgPasswordPolicy' => 'array_merge_recursive',
106 'wgRateLimits' => 'array_plus_2d',
107 'wgRevokePermissions' => 'array_plus_2d',
108 ];
109
115 protected const CREDIT_ATTRIBS = [
116 'type',
117 'author',
118 'description',
119 'descriptionmsg',
120 'license-name',
121 'name',
122 'namemsg',
123 'url',
124 'version',
125 ];
126
133 protected const NOT_ATTRIBS = [
134 'callback',
135 'config',
136 'config_prefix',
137 'load_composer_autoloader',
138 'manifest_version',
139 'namespaces',
140 'requires',
141 'AutoloadClasses',
142 'AutoloadNamespaces',
143 'ExtensionMessagesFiles',
144 'ForeignResourcesDir',
145 'Hooks',
146 'MessagePosterModule',
147 'MessagesDirs',
148 'OOUIThemePaths',
149 'QUnitTestModule',
150 'ResourceFileModulePaths',
151 'ResourceModuleSkinStyles',
152 'ResourceModules',
153 'ServiceWiringFiles',
154 ];
155
163 protected $globals = [
164 'wgExtensionMessagesFiles' => [],
165 'wgMessagesDirs' => [],
166 ];
167
173 protected $defines = [];
174
182 protected $callbacks = [];
183
187 protected $credits = [];
188
196 protected $autoload = [
197 'files' => [],
198 'classes' => [],
199 'namespaces' => [],
200 ];
201
208 protected $autoloadDev = [
209 'files' => [],
210 'classes' => [],
211 'namespaces' => [],
212 ];
213
220 protected $attributes = [];
221
228 protected $extAttributes = [];
229
237 public function extractInfoFromFile( string $path ) {
238 $json = file_get_contents( $path );
239 $info = json_decode( $json, true );
240
241 if ( !$info ) {
242 throw new RuntimeException( "Failed to load JSON data from $path" );
243 }
244
245 $this->extractInfo( $path, $info, $info['manifest_version'] );
246 }
247
253 public function extractInfo( $path, array $info, $version ) {
254 $dir = dirname( $path );
255 $this->extractHooks( $info, $path );
256 $this->extractExtensionMessagesFiles( $dir, $info );
257 $this->extractMessagesDirs( $dir, $info );
258 $this->extractSkins( $dir, $info );
259 $this->extractSkinImportPaths( $dir, $info );
260 $this->extractNamespaces( $info );
261 $this->extractImplicitRights( $info );
262 $this->extractResourceLoaderModules( $dir, $info );
263 if ( isset( $info['ServiceWiringFiles'] ) ) {
265 'wgServiceWiringFiles',
266 $dir,
267 $info['ServiceWiringFiles']
268 );
269 }
270 $name = $this->extractCredits( $path, $info );
271 if ( isset( $info['callback'] ) ) {
272 $this->callbacks[$name] = $info['callback'];
273 }
274
275 $this->extractAutoload( $info, $dir );
276
277 // config should be after all core globals are extracted,
278 // so duplicate setting detection will work fully
279 if ( $version >= 2 ) {
280 $this->extractConfig2( $info, $dir );
281 } else {
282 // $version === 1
283 $this->extractConfig1( $info );
284 }
285
286 // Record the extension name in the ParsoidModules property
287 if ( isset( $info['ParsoidModules'] ) ) {
288 foreach ( $info['ParsoidModules'] as &$module ) {
289 if ( is_string( $module ) ) {
290 $className = $module;
291 $module = [
292 'class' => $className,
293 ];
294 }
295 $module['name'] = $name;
296 }
297 }
298
299 $this->extractForeignResourcesDir( $info, $name, $dir );
300
301 if ( $version >= 2 ) {
302 $this->extractAttributes( $path, $info );
303 }
304
305 foreach ( $info as $key => $val ) {
306 // If it's a global setting,
307 if ( in_array( $key, self::$globalSettings ) ) {
308 $this->storeToArrayRecursive( $path, "wg$key", $val, $this->globals );
309 continue;
310 }
311 // Ignore anything that starts with a @
312 if ( $key[0] === '@' ) {
313 continue;
314 }
315
316 if ( $version >= 2 ) {
317 // Only allowed attributes are set
318 if ( in_array( $key, self::CORE_ATTRIBS ) ) {
319 $this->storeToArray( $path, $key, $val, $this->attributes );
320 }
321 } else {
322 // version === 1
323 if ( !in_array( $key, self::NOT_ATTRIBS )
324 && !in_array( $key, self::CREDIT_ATTRIBS )
325 ) {
326 // If it's not disallowed, it's an attribute
327 $this->storeToArrayRecursive( $path, $key, $val, $this->attributes );
328 }
329 }
330 }
331 }
332
337 protected function extractAttributes( $path, array $info ) {
338 if ( isset( $info['attributes'] ) ) {
339 foreach ( $info['attributes'] as $extName => $value ) {
340 $this->storeToArrayRecursive( $path, $extName, $value, $this->extAttributes );
341 }
342 }
343 }
344
345 public function getExtractedInfo( bool $includeDev = false ) {
346 // Make sure the merge strategies are set
347 foreach ( $this->globals as $key => $val ) {
348 if ( isset( self::MERGE_STRATEGIES[$key] ) ) {
349 $this->globals[$key][ExtensionRegistry::MERGE_STRATEGY] = self::MERGE_STRATEGIES[$key];
350 }
351 }
352
353 // Merge $this->extAttributes into $this->attributes depending on what is loaded
354 foreach ( $this->extAttributes as $extName => $value ) {
355 // Only set the attribute if $extName is loaded (and hence present in credits)
356 if ( isset( $this->credits[$extName] ) ) {
357 foreach ( $value as $attrName => $attrValue ) {
359 '', // Don't provide a path since it's impossible to generate an error here
360 $extName . $attrName,
361 $attrValue,
362 $this->attributes
363 );
364 }
365 unset( $this->extAttributes[$extName] );
366 }
367 }
368
369 $autoload = $this->getExtractedAutoloadInfo( $includeDev );
370 return [
371 'globals' => $this->globals,
372 'defines' => $this->defines,
373 'callbacks' => $this->callbacks,
374 'credits' => $this->credits,
375 'attributes' => $this->attributes,
376 'autoloaderPaths' => $autoload['files'],
377 'autoloaderClasses' => $autoload['classes'],
378 'autoloaderNS' => $autoload['namespaces'],
379 ];
380 }
381
382 public function getRequirements( array $info, $includeDev ) {
383 // Quick shortcuts
384 if ( !$includeDev || !isset( $info['dev-requires'] ) ) {
385 return $info['requires'] ?? [];
386 }
387
388 if ( !isset( $info['requires'] ) ) {
389 return $info['dev-requires'] ?? [];
390 }
391
392 // OK, we actually have to merge everything
393 $merged = [];
394
395 // Helper that combines version requirements by
396 // picking the non-null if one is, or combines
397 // the two. Note that it is not possible for
398 // both inputs to be null.
399 $pick = static function ( $a, $b ) {
400 if ( $a === null ) {
401 return $b;
402 } elseif ( $b === null ) {
403 return $a;
404 } else {
405 return "$a $b";
406 }
407 };
408
409 $req = $info['requires'];
410 $dev = $info['dev-requires'];
411 if ( isset( $req['MediaWiki'] ) || isset( $dev['MediaWiki'] ) ) {
412 $merged['MediaWiki'] = $pick(
413 $req['MediaWiki'] ?? null,
414 $dev['MediaWiki'] ?? null
415 );
416 }
417
418 $platform = array_merge(
419 array_keys( $req['platform'] ?? [] ),
420 array_keys( $dev['platform'] ?? [] )
421 );
422 if ( $platform ) {
423 foreach ( $platform as $pkey ) {
424 if ( $pkey === 'php' ) {
425 $value = $pick(
426 $req['platform']['php'] ?? null,
427 $dev['platform']['php'] ?? null
428 );
429 } else {
430 // Prefer dev value, but these should be constant
431 // anyway (ext-* and ability-*)
432 $value = $dev['platform'][$pkey] ?? $req['platform'][$pkey];
433 }
434 $merged['platform'][$pkey] = $value;
435 }
436 }
437
438 foreach ( [ 'extensions', 'skins' ] as $thing ) {
439 $things = array_merge(
440 array_keys( $req[$thing] ?? [] ),
441 array_keys( $dev[$thing] ?? [] )
442 );
443 foreach ( $things as $name ) {
444 $merged[$thing][$name] = $pick(
445 $req[$thing][$name] ?? null,
446 $dev[$thing][$name] ?? null
447 );
448 }
449 }
450 return $merged;
451 }
452
464 private function setArrayHookHandler(
465 array $callback,
466 array $hookHandlersAttr,
467 string $name,
468 string $path
469 ) {
470 if ( isset( $callback['handler'] ) ) {
471 $handlerName = $callback['handler'];
472 $handlerDefinition = $hookHandlersAttr[$handlerName] ?? false;
473 if ( !$handlerDefinition ) {
474 throw new UnexpectedValueException(
475 "Missing handler definition for $name in HookHandlers attribute in $path"
476 );
477 }
478 $callback['handler'] = $handlerDefinition;
479 $callback['extensionPath'] = $path;
480 $this->attributes['Hooks'][$name][] = $callback;
481 } else {
482 foreach ( $callback as $callable ) {
483 if ( is_array( $callable ) ) {
484 if ( isset( $callable['handler'] ) ) { // Non-legacy style handler
485 $this->setArrayHookHandler( $callable, $hookHandlersAttr, $name, $path );
486 } else { // Legacy style handler array
487 $this->globals['wgHooks'][$name][] = $callable;
488 }
489 } elseif ( is_string( $callable ) ) {
490 $this->setStringHookHandler( $callable, $hookHandlersAttr, $name, $path );
491 }
492 }
493 }
494 }
495
506 private function setStringHookHandler(
507 string $callback,
508 array $hookHandlersAttr,
509 string $name,
510 string $path
511 ) {
512 if ( isset( $hookHandlersAttr[$callback] ) ) {
513 $handler = [
514 'handler' => $hookHandlersAttr[$callback],
515 'extensionPath' => $path
516 ];
517 $this->attributes['Hooks'][$name][] = $handler;
518 } else { // legacy style handler
519 $this->globals['wgHooks'][$name][] = $callback;
520 }
521 }
522
531 protected function extractHooks( array $info, string $path ) {
532 $extName = $info['name'];
533 if ( isset( $info['Hooks'] ) ) {
534 $hookHandlersAttr = [];
535 foreach ( $info['HookHandlers'] ?? [] as $name => $def ) {
536 $hookHandlersAttr[$name] = [ 'name' => "$extName-$name" ] + $def;
537 }
538 foreach ( $info['Hooks'] as $name => $callback ) {
539 if ( is_string( $callback ) ) {
540 $this->setStringHookHandler( $callback, $hookHandlersAttr, $name, $path );
541 } elseif ( is_array( $callback ) ) {
542 $this->setArrayHookHandler( $callback, $hookHandlersAttr, $name, $path );
543 }
544 }
545 }
546 if ( isset( $info['DeprecatedHooks'] ) ) {
547 $deprecatedHooks = [];
548 foreach ( $info['DeprecatedHooks'] as $name => $deprecatedHookInfo ) {
549 $deprecatedHookInfo += [ 'component' => $extName ];
550 $deprecatedHooks[$name] = $deprecatedHookInfo;
551 }
552 if ( isset( $this->attributes['DeprecatedHooks'] ) ) {
553 $this->attributes['DeprecatedHooks'] += $deprecatedHooks;
554 } else {
555 $this->attributes['DeprecatedHooks'] = $deprecatedHooks;
556 }
557 }
558 }
559
565 protected function extractNamespaces( array $info ) {
566 if ( isset( $info['namespaces'] ) ) {
567 foreach ( $info['namespaces'] as $ns ) {
568 if ( defined( $ns['constant'] ) ) {
569 // If the namespace constant is already defined, use it.
570 // This allows namespace IDs to be overwritten locally.
571 $id = constant( $ns['constant'] );
572 } else {
573 $id = $ns['id'];
574 }
575 $this->defines[ $ns['constant'] ] = $id;
576
577 if ( !( isset( $ns['conditional'] ) && $ns['conditional'] ) ) {
578 // If it is not conditional, register it
579 $this->attributes['ExtensionNamespaces'][$id] = $ns['name'];
580 }
581 if ( isset( $ns['movable'] ) && !$ns['movable'] ) {
582 $this->attributes['ImmovableNamespaces'][] = $id;
583 }
584 if ( isset( $ns['gender'] ) ) {
585 $this->globals['wgExtraGenderNamespaces'][$id] = $ns['gender'];
586 }
587 if ( isset( $ns['subpages'] ) && $ns['subpages'] ) {
588 $this->globals['wgNamespacesWithSubpages'][$id] = true;
589 }
590 if ( isset( $ns['content'] ) && $ns['content'] ) {
591 $this->globals['wgContentNamespaces'][] = $id;
592 }
593 if ( isset( $ns['defaultcontentmodel'] ) ) {
594 $this->globals['wgNamespaceContentModels'][$id] = $ns['defaultcontentmodel'];
595 }
596 if ( isset( $ns['protection'] ) ) {
597 $this->globals['wgNamespaceProtection'][$id] = $ns['protection'];
598 }
599 if ( isset( $ns['capitallinkoverride'] ) ) {
600 $this->globals['wgCapitalLinkOverrides'][$id] = $ns['capitallinkoverride'];
601 }
602 if ( isset( $ns['includable'] ) && !$ns['includable'] ) {
603 $this->globals['wgNonincludableNamespaces'][] = $id;
604 }
605 }
606 }
607 }
608
609 protected function extractResourceLoaderModules( $dir, array $info ) {
610 $defaultPaths = $info['ResourceFileModulePaths'] ?? false;
611 if ( isset( $defaultPaths['localBasePath'] ) ) {
612 if ( $defaultPaths['localBasePath'] === '' ) {
613 // Avoid double slashes (e.g. /extensions/Example//path)
614 $defaultPaths['localBasePath'] = $dir;
615 } else {
616 $defaultPaths['localBasePath'] = "$dir/{$defaultPaths['localBasePath']}";
617 }
618 }
619
620 foreach ( [ 'ResourceModules', 'ResourceModuleSkinStyles', 'OOUIThemePaths' ] as $setting ) {
621 if ( isset( $info[$setting] ) ) {
622 foreach ( $info[$setting] as $name => $data ) {
623 if ( isset( $data['localBasePath'] ) ) {
624 if ( $data['localBasePath'] === '' ) {
625 // Avoid double slashes (e.g. /extensions/Example//path)
626 $data['localBasePath'] = $dir;
627 } else {
628 $data['localBasePath'] = "$dir/{$data['localBasePath']}";
629 }
630 }
631 if ( $defaultPaths ) {
632 $data += $defaultPaths;
633 }
634 $this->attributes[$setting][$name] = $data;
635 }
636 }
637 }
638
639 if ( isset( $info['QUnitTestModule'] ) ) {
640 $data = $info['QUnitTestModule'];
641 if ( isset( $data['localBasePath'] ) ) {
642 if ( $data['localBasePath'] === '' ) {
643 // Avoid double slashes (e.g. /extensions/Example//path)
644 $data['localBasePath'] = $dir;
645 } else {
646 $data['localBasePath'] = "$dir/{$data['localBasePath']}";
647 }
648 }
649 // Satisfy PHPUnit ResourcesTest::testUnsatisfiableDependencies
650 $data['targets'] = [ 'test' ];
651 $this->attributes['QUnitTestModules']["test.{$info['name']}"] = $data;
652 }
653
654 if ( isset( $info['MessagePosterModule'] ) ) {
655 $data = $info['MessagePosterModule'];
656 $basePath = $data['localBasePath'] ?? '';
657 $baseDir = $basePath === '' ? $dir : "$dir/$basePath";
658 foreach ( $data['scripts'] ?? [] as $scripts ) {
659 $this->attributes['MessagePosterModule']['scripts'][] =
660 new FilePath( $scripts, $baseDir );
661 }
662 foreach ( $data['dependencies'] ?? [] as $dependency ) {
663 $this->attributes['MessagePosterModule']['dependencies'][] = $dependency;
664 }
665 }
666 }
667
668 protected function extractExtensionMessagesFiles( $dir, array $info ) {
669 if ( isset( $info['ExtensionMessagesFiles'] ) ) {
670 foreach ( $info['ExtensionMessagesFiles'] as &$file ) {
671 $file = "$dir/$file";
672 }
673 $this->globals["wgExtensionMessagesFiles"] += $info['ExtensionMessagesFiles'];
674 }
675 }
676
684 protected function extractMessagesDirs( $dir, array $info ) {
685 if ( isset( $info['MessagesDirs'] ) ) {
686 foreach ( $info['MessagesDirs'] as $name => $files ) {
687 foreach ( (array)$files as $file ) {
688 $this->globals["wgMessagesDirs"][$name][] = "$dir/$file";
689 }
690 }
691 }
692 }
693
700 protected function extractSkins( $dir, array $info ) {
701 if ( isset( $info['ValidSkinNames'] ) ) {
702 foreach ( $info['ValidSkinNames'] as $skinKey => $data ) {
703 if ( isset( $data['args'][0] ) ) {
704 $templateDirectory = $data['args'][0]['templateDirectory'] ?? 'templates';
705 $data['args'][0]['templateDirectory'] = $dir . '/' . $templateDirectory;
706 }
707 $this->globals['wgValidSkinNames'][$skinKey] = $data;
708 }
709 }
710 }
711
717 protected function extractImplicitRights( array $info ) {
718 // Rate limits are only configurable for rights that are either in wgImplicitRights
719 // or in wgAvailableRights. Extensions that define rate limits should not have to
720 // explicitly add them to wgImplicitRights as well, we can do that automatically.
721
722 if ( isset( $info['RateLimits'] ) ) {
723 $rights = array_keys( $info['RateLimits'] );
724
725 if ( isset( $info['AvailableRights'] ) ) {
726 $rights = array_diff( $rights, $info['AvailableRights'] );
727 }
728
729 $this->globals['wgImplicitRights'] = array_merge(
730 $this->globals['wgImplicitRights'] ?? [],
731 $rights
732 );
733 }
734 }
735
740 protected function extractSkinImportPaths( $dir, array $info ) {
741 if ( isset( $info['SkinLessImportPaths'] ) ) {
742 foreach ( $info['SkinLessImportPaths'] as $skin => $subpath ) {
743 $this->attributes['SkinLessImportPaths'][$skin] = "$dir/$subpath";
744 }
745 }
746 }
747
754 protected function extractCredits( $path, array $info ) {
755 $credits = [
756 'path' => $path,
757 'type' => 'other',
758 ];
759 foreach ( self::CREDIT_ATTRIBS as $attr ) {
760 if ( isset( $info[$attr] ) ) {
761 $credits[$attr] = $info[$attr];
762 }
763 }
764
765 $name = $credits['name'];
766
767 // If someone is loading the same thing twice, throw
768 // a nice error (T121493)
769 if ( isset( $this->credits[$name] ) ) {
770 $firstPath = $this->credits[$name]['path'];
771 $secondPath = $credits['path'];
772 throw new Exception( "It was attempted to load $name twice, from $firstPath and $secondPath." );
773 }
774
775 $this->credits[$name] = $credits;
776
777 return $name;
778 }
779
780 protected function extractForeignResourcesDir( array $info, string $name, string $dir ): void {
781 if ( array_key_exists( 'ForeignResourcesDir', $info ) ) {
782 if ( !is_string( $info['ForeignResourcesDir'] ) ) {
783 throw new Exception( "Incorrect ForeignResourcesDir type, must be a string (in $name)" );
784 }
785 $this->attributes['ForeignResourcesDir'][$name] = "{$dir}/{$info['ForeignResourcesDir']}";
786 }
787 }
788
795 protected function extractConfig1( array $info ) {
796 if ( isset( $info['config'] ) ) {
797 if ( isset( $info['config']['_prefix'] ) ) {
798 $prefix = $info['config']['_prefix'];
799 unset( $info['config']['_prefix'] );
800 } else {
801 $prefix = 'wg';
802 }
803 foreach ( $info['config'] as $key => $val ) {
804 if ( $key[0] !== '@' ) {
805 $this->addConfigGlobal( "$prefix$key", $val, $info['name'] );
806 }
807 }
808 }
809 }
810
819 private function applyPath( array $value, string $dir ): array {
820 $result = [];
821
822 foreach ( $value as $k => $v ) {
823 $result[$k] = $dir . '/' . $v;
824 }
825 return $result;
826 }
827
835 protected function extractConfig2( array $info, $dir ) {
836 $prefix = $info['config_prefix'] ?? 'wg';
837 if ( isset( $info['config'] ) ) {
838 foreach ( $info['config'] as $key => $data ) {
839 if ( !array_key_exists( 'value', $data ) ) {
840 throw new UnexpectedValueException( "Missing value for config $key" );
841 }
842
843 $value = $data['value'];
844 if ( isset( $data['path'] ) && $data['path'] ) {
845 if ( is_array( $value ) ) {
846 $value = $this->applyPath( $value, $dir );
847 } else {
848 $value = "$dir/$value";
849 }
850 }
851 if ( isset( $data['merge_strategy'] ) ) {
852 $value[ExtensionRegistry::MERGE_STRATEGY] = $data['merge_strategy'];
853 }
854 $this->addConfigGlobal( "$prefix$key", $value, $info['name'] );
855 $data['providedby'] = $info['name'];
856 if ( isset( $info['ConfigRegistry'][0] ) ) {
857 $data['configregistry'] = array_keys( $info['ConfigRegistry'] )[0];
858 }
859 }
860 }
861 }
862
870 private function addConfigGlobal( $key, $value, $extName ) {
871 if ( array_key_exists( $key, $this->globals ) ) {
872 throw new RuntimeException(
873 "The configuration setting '$key' was already set by MediaWiki core or"
874 . " another extension, and cannot be set again by $extName." );
875 }
876 $this->globals[$key] = $value;
877 }
878
879 protected function extractPathBasedGlobal( $global, $dir, $paths ) {
880 foreach ( $paths as $path ) {
881 $this->globals[$global][] = "$dir/$path";
882 }
883 }
884
894 protected function storeToArrayRecursive( $path, $name, $value, &$array ) {
895 if ( !is_array( $value ) ) {
896 throw new InvalidArgumentException( "The value for '$name' should be an array (from $path)" );
897 }
898 if ( isset( $array[$name] ) ) {
899 $array[$name] = array_merge_recursive( $array[$name], $value );
900 } else {
901 $array[$name] = $value;
902 }
903 }
904
914 protected function storeToArray( $path, $name, $value, &$array ) {
915 if ( !is_array( $value ) ) {
916 throw new InvalidArgumentException( "The value for '$name' should be an array (from $path)" );
917 }
918 if ( isset( $array[$name] ) ) {
919 $array[$name] = array_merge( $array[$name], $value );
920 } else {
921 $array[$name] = $value;
922 }
923 }
924
933 public function getExtraAutoloaderPaths( $dir, array $info ) {
934 wfDeprecated( __METHOD__, '1.39' );
935 $paths = [];
936 if ( isset( $info['load_composer_autoloader'] ) && $info['load_composer_autoloader'] === true ) {
937 $paths[] = "$dir/vendor/autoload.php";
938 }
939 return $paths;
940 }
941
956 public function getExtractedAutoloadInfo( bool $includeDev = false ): array {
957 $autoload = $this->autoload;
958
959 if ( $includeDev ) {
960 $autoload['classes'] += $this->autoloadDev['classes'];
961 $autoload['namespaces'] += $this->autoloadDev['namespaces'];
962
963 // NOTE: This is here for completeness. Per MW 1.39,
964 // $this->autoloadDev['files'] is always empty.
965 // So avoid the performance hit of array_merge().
966 if ( !empty( $this->autoloadDev['files'] ) ) {
967 // NOTE: Don't use += with numeric keys!
968 // Could use PHPUtils::pushArray.
969 $autoload['files'] = array_merge(
970 $autoload['files'],
971 $this->autoloadDev['files']
972 );
973 }
974 }
975
976 return $autoload;
977 }
978
983 private function extractAutoload( array $info, string $dir ) {
984 if ( isset( $info['load_composer_autoloader'] ) && $info['load_composer_autoloader'] === true ) {
985 $file = "$dir/vendor/autoload.php";
986 if ( file_exists( $file ) ) {
987 $this->autoload['files'][] = $file;
988 }
989 }
990
991 if ( isset( $info['AutoloadClasses'] ) ) {
992 $paths = $this->applyPath( $info['AutoloadClasses'], $dir );
993 $this->autoload['classes'] += $paths;
994 }
995
996 if ( isset( $info['AutoloadNamespaces'] ) ) {
997 $paths = $this->applyPath( $info['AutoloadNamespaces'], $dir );
998 $this->autoload['namespaces'] += $paths;
999 }
1000
1001 if ( isset( $info['TestAutoloadClasses'] ) ) {
1002 $paths = $this->applyPath( $info['TestAutoloadClasses'], $dir );
1003 $this->autoloadDev['classes'] += $paths;
1004 }
1005
1006 if ( isset( $info['TestAutoloadNamespaces'] ) ) {
1007 $paths = $this->applyPath( $info['TestAutoloadNamespaces'], $dir );
1008 $this->autoloadDev['namespaces'] += $paths;
1009 }
1010 }
1011}
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:88
Load extension manifests and then aggregate their contents.
extractHooks(array $info, string $path)
Extract hook information from Hooks and HookHandler attributes.
extractNamespaces(array $info)
Register namespaces with the appropriate global settings.
extractCredits( $path, array $info)
array $attributes
Anything else in the $info that hasn't already been processed.
extractInfoFromFile(string $path)
Extracts extension info from the given JSON file.
callable[] $callbacks
Things to be called once the registration of these extensions is done.
array $defines
Things that should be define()'d.
string[][] $autoloadDev
Autoloader information for development.
extractExtensionMessagesFiles( $dir, array $info)
extractConfig1(array $info)
Set configuration settings for manifest_version == 1.
array $globals
Stuff that is going to be set to $GLOBALS.
extractMessagesDirs( $dir, array $info)
Set message-related settings, which need to be expanded to use absolute paths.
extractConfig2(array $info, $dir)
Set configuration settings for manifest_version == 2.
getExtractedInfo(bool $includeDev=false)
extractResourceLoaderModules( $dir, array $info)
array $extAttributes
Extension attributes, keyed by name => settings.
extractSkins( $dir, array $info)
Extract skins and handle path correction for templateDirectory.
getExtraAutoloaderPaths( $dir, array $info)
extractAttributes( $path, array $info)
getRequirements(array $info, $includeDev)
Get the requirements for the provided info.
extractSkinImportPaths( $dir, array $info)
extractInfo( $path, array $info, $version)
getExtractedAutoloadInfo(bool $includeDev=false)
Returns the extracted autoload info.
storeToArray( $path, $name, $value, &$array)
Stores $value to $array; using array_merge() if $array already contains $name.
extractImplicitRights(array $info)
Extract any user rights that should be granted implicitly.
string[][] $autoload
Autoloader information.
extractPathBasedGlobal( $global, $dir, $paths)
extractForeignResourcesDir(array $info, string $name, string $dir)
storeToArrayRecursive( $path, $name, $value, &$array)
Stores $value to $array; using array_merge_recursive() if $array already contains $name.
static array $globalSettings
Keys that should be set to $GLOBALS.
A class containing constants representing the names of configuration variables.
A path to a bundled file (such as JavaScript or CSS), along with a remote and local base path.
Definition FilePath.php:34
Generic processor that reads associated arrays and registers whatever is required.
Definition Processor.php:9
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42