12
12
namespace Symfony \Component \DependencyInjection \Loader ;
13
13
14
14
use Symfony \Component \DependencyInjection \ContainerBuilder ;
15
+ use Symfony \Component \DependencyInjection \Definition ;
16
+ use Symfony \Component \DependencyInjection \Exception \InvalidArgumentException ;
17
+ use Symfony \Component \DependencyInjection \Exception \LogicException ;
15
18
use Symfony \Component \Config \Loader \FileLoader as BaseFileLoader ;
16
19
use Symfony \Component \Config \FileLocatorInterface ;
20
+ use Symfony \Component \Finder \Finder ;
21
+ use Symfony \Component \Finder \Glob ;
17
22
18
23
/**
19
24
* FileLoader is the abstract class used by all built-in loaders that are file based.
@@ -34,4 +39,101 @@ public function __construct(ContainerBuilder $container, FileLocatorInterface $l
34
39
35
40
parent ::__construct ($ locator );
36
41
}
42
+
43
+ /**
44
+ * @experimental in version 3.3
45
+ */
46
+ public function registerClasses (Definition $ prototype , $ namespace , $ resourcesGlob )
47
+ {
48
+ if ('\\' !== substr ($ namespace , -1 )) {
49
+ throw new InvalidArgumentException (sprintf ('Namespace prefix must end with a " \\": %s. ' , $ namespace ));
50
+ }
51
+ if (!preg_match ('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+ \\\\)++$/ ' , $ namespace )) {
52
+ throw new InvalidArgumentException (sprintf ('Namespace is not a valid PSR-4 prefix: %s. ' , $ namespace ));
53
+ }
54
+
55
+ $ classes = $ this ->findClasses ($ namespace , $ resourcesGlob );
56
+ $ prototype = serialize ($ prototype );
57
+
58
+ foreach ($ classes as $ class ) {
59
+ $ this ->container ->setDefinition ($ class , unserialize ($ prototype ));
60
+ }
61
+ }
62
+
63
+ private function findClasses ($ namespace , $ resourcesGlob )
64
+ {
65
+ $ classes = array ();
66
+ $ extRegexp = defined ('HHVM_VERSION ' ) ? '/ \\.(?:php|hh)$/ ' : '/ \\.php$/ ' ;
67
+
68
+ foreach ($ this ->glob ($ resourcesGlob , true , $ prefixLen ) as $ path => $ info ) {
69
+ if (!preg_match ($ extRegexp , $ path , $ m ) || !$ info ->isFile () || !$ info ->isReadable ()) {
70
+ continue ;
71
+ }
72
+ $ class = $ namespace .ltrim (str_replace ('/ ' , '\\' , substr ($ path , $ prefixLen , -strlen ($ m [0 ]))), '\\' );
73
+
74
+ if (!preg_match ('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?: \\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/ ' , $ class )) {
75
+ continue ;
76
+ }
77
+ if (!$ r = $ this ->container ->getReflectionClass ($ class , true )) {
78
+ continue ;
79
+ }
80
+ if (!$ r ->isInterface () && !$ r ->isTrait ()) {
81
+ $ classes [] = $ class ;
82
+ }
83
+ }
84
+
85
+ return $ classes ;
86
+ }
87
+
88
+ private function glob ($ resourcesGlob , $ recursive , &$ prefixLen = null )
89
+ {
90
+ if (strlen ($ resourcesGlob ) === $ i = strcspn ($ resourcesGlob , '*?{[ ' )) {
91
+ $ resourcesPrefix = $ resourcesGlob ;
92
+ $ resourcesGlob = '' ;
93
+ } elseif (0 === $ i ) {
94
+ $ resourcesPrefix = '. ' ;
95
+ $ resourcesGlob = '/ ' .$ resourcesGlob ;
96
+ } else {
97
+ $ resourcesPrefix = dirname (substr ($ resourcesGlob , 0 , 1 + $ i ));
98
+ $ resourcesGlob = substr ($ resourcesGlob , strlen ($ resourcesPrefix ));
99
+ }
100
+
101
+ $ resourcesPrefix = $ this ->locator ->locate ($ resourcesPrefix , $ this ->currentDir , true );
102
+ $ resourcesPrefix = realpath ($ resourcesPrefix ) ?: $ resourcesPrefix ;
103
+ $ prefixLen = strlen ($ resourcesPrefix );
104
+
105
+ // track directories only for new & removed files
106
+ $ this ->container ->fileExists ($ resourcesPrefix , '/^$/ ' );
107
+
108
+ if (false === strpos ($ resourcesGlob , '/**/ ' ) && (defined ('GLOB_BRACE ' ) || false === strpos ($ resourcesGlob , '{ ' ))) {
109
+ foreach (glob ($ resourcesPrefix .$ resourcesGlob , defined ('GLOB_BRACE ' ) ? GLOB_BRACE : 0 ) as $ path ) {
110
+ if ($ recursive && is_dir ($ path )) {
111
+ $ flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS ;
112
+ foreach (new \RecursiveIteratorIterator (new \RecursiveDirectoryIterator ($ path , $ flags )) as $ path => $ info ) {
113
+ yield $ path => $ info ;
114
+ }
115
+ } else {
116
+ yield $ path => new \SplFileInfo ($ path );
117
+ }
118
+ }
119
+
120
+ return ;
121
+ }
122
+
123
+ if (!class_exists (Finder::class)) {
124
+ throw new LogicException (sprintf ('Extended glob pattern "%s" cannot be used as the Finder component is not installed. ' , $ resourcesGlob ));
125
+ }
126
+
127
+ $ finder = new Finder ();
128
+ $ regex = Glob::toRegex ($ resourcesGlob );
129
+ if ($ recursive ) {
130
+ $ regex = substr_replace ($ regex , '(/|$) ' , -2 , 1 );
131
+ }
132
+
133
+ foreach ($ finder ->followLinks ()->in ($ resourcesPrefix ) as $ path => $ info ) {
134
+ if (preg_match ($ regex , substr ($ path , $ prefixLen ))) {
135
+ yield $ path => $ info ;
136
+ }
137
+ }
138
+ }
37
139
}
0 commit comments