55use ByJG \FeatureFlag \Attributes \FeatureFlagAttribute ;
66use ReflectionAttribute ;
77use ReflectionClass ;
8+ use ReflectionException ;
89use ReflectionMethod ;
910
1011class FeatureFlagDispatcher
@@ -14,17 +15,42 @@ class FeatureFlagDispatcher
1415 protected SearchOrder $ searchOrder = SearchOrder::Selector;
1516 protected array $ limitFlags = [];
1617
17- public function add (FeatureFlagSelector $ selector ): static
18+ public function add (FeatureFlagSelector |FeatureFlagSelectorSet $ selector ): static
19+ {
20+ if ($ selector instanceof FeatureFlagSelector) {
21+ $ this ->addSelector ($ selector );
22+ } else {
23+ $ this ->addSelectorSet ($ selector );
24+ }
25+
26+ return $ this ;
27+ }
28+
29+ protected function addSelector (FeatureFlagSelector $ selector ): void
1830 {
1931 if (!isset ($ this ->selectors [$ selector ->getFlagName ()])) {
2032 $ this ->selectors [$ selector ->getFlagName ()] = [];
2133 }
2234
2335 $ this ->selectors [$ selector ->getFlagName ()][] = $ selector ;
36+ }
2437
25- return $ this ;
38+ protected function addSelectorSet (FeatureFlagSelectorSet $ selectorSet ): void
39+ {
40+ $ list = $ selectorSet ->get ();
41+
42+ $ keys = array_keys ($ list );
43+
44+ if (!isset ($ this ->selectors [$ keys [0 ]])) {
45+ $ this ->selectors [$ keys [0 ]] = [];
46+ }
47+
48+ $ this ->selectors [$ keys [0 ]][] = $ list ;
2649 }
2750
51+ /**
52+ * @throws ReflectionException
53+ */
2854 public function addClass (string $ className ): void
2955 {
3056 $ reflection = new ReflectionClass ($ className );
@@ -52,75 +78,122 @@ public function addClass(string $className): void
5278 public function withSearchOrder (SearchOrder $ searchOrder , array $ flags = []): static
5379 {
5480 $ this ->searchOrder = $ searchOrder ;
55- $ this ->limitFlags = $ flags ;
5681 if ($ searchOrder == SearchOrder::Custom && empty ($ flags )) {
5782 throw new \InvalidArgumentException ("You must provide the flags when using Custom search order " );
5883 }
5984 if ($ searchOrder != SearchOrder::Custom && !empty ($ flags )) {
6085 throw new \InvalidArgumentException ("You must not provide the flags when not using Custom search order " );
6186 }
87+
88+ if (!empty ($ flags )) {
89+ $ this ->limitFlags = array_filter (
90+ FeatureFlags::getFlags (),
91+ fn ($ key ) => in_array ($ key , $ flags ),
92+ ARRAY_FILTER_USE_KEY
93+ );
94+ }
95+
6296 return $ this ;
6397 }
6498
6599 public function dispatch (...$ args ): int
66100 {
67101 return match ($ this ->searchOrder ) {
68- SearchOrder::Selector => $ this ->dispatchQuerySelector (...$ args ),
69- SearchOrder::FeatureFlags => $ this ->dispatchQueryingFlags (...$ args ),
70- SearchOrder::Custom => $ this ->dispatchQueryCustom ( ...$ args ),
102+ SearchOrder::Selector => $ this ->dispatchQuerySelector ($ this -> selectors , true , ...$ args ),
103+ SearchOrder::FeatureFlags => $ this ->dispatchQueryingFlags (FeatureFlags:: getFlags (), $ this -> selectors , true , ...$ args ),
104+ SearchOrder::Custom => $ this ->dispatchQueryingFlags ( $ this -> limitFlags , $ this -> selectors , true , ...$ args ),
71105 default => 0 ,
72106 };
73107 }
74108
75- protected function dispatchQueryingFlags (...$ args ): int
109+ protected function dispatchQueryingFlags (array $ flagList , array $ selectorList , bool $ invoke , ...$ args ): int
76110 {
77111 $ count = 0 ;
78- foreach (FeatureFlags:: getFlags () as $ flagName => $ flagValue ) {
79- if (!isset ($ this -> selectors [$ flagName ])) {
112+ foreach ($ flagList as $ flagName => $ flagValue ) {
113+ if (!isset ($ selectorList [$ flagName ])) {
80114 continue ;
81115 }
82116
83117 if (!FeatureFlags::hasFlag ($ flagName )) {
84118 continue ;
85119 }
86120
87- foreach ($ this ->selectors [$ flagName ] as $ selector ) {
88- if ($ selector ->isMatch ($ flagName , $ flagValue )) {
89- $ selector ->invoke ($ args );
90- $ count ++;
91- if (!$ selector ->isContinueProcessing ()) {
92- break ;
93- }
121+ foreach ($ selectorList [$ flagName ] as $ selector ) {
122+ $ result = $ this ->match ($ selector , $ flagName , $ flagValue , $ flagList , $ invoke , ...$ args );
123+ $ count += ($ result < 0 ? -$ result : $ result );
124+ if ($ result < 0 ) {
125+ break ;
94126 }
95127 }
96128 }
97129
98130 return $ count ;
99131 }
100132
101- protected function dispatchQuerySelector (...$ args ): int
133+ protected function dispatchQuerySelector (array $ selectorList , bool $ invoke , ...$ args ): int
102134 {
103135 $ count = 0 ;
104- foreach ($ this -> selectors as $ flagName => $ selectors ) {
136+ foreach ($ selectorList as $ flagName => $ selectors ) {
105137 /** @var FeatureFlagSelector $selector */
106138 foreach ($ selectors as $ selector ) {
107139 if (!FeatureFlags::hasFlag ($ flagName )) {
108140 continue ;
109141 }
110142
111- if ($ selector ->isMatch ($ flagName , FeatureFlags::getFlag ($ flagName ))) {
112- $ selector ->invoke ($ args );
113- $ count ++;
114- if (!$ selector ->isContinueProcessing ()) {
115- break ;
116- }
143+ $ result = $ this ->match ($ selector , $ flagName , FeatureFlags::getFlag ($ flagName ), [], $ invoke , ...$ args );
144+ $ count += ($ result < 0 ? -$ result : $ result );
145+ if ($ result < 0 ) {
146+ break ;
117147 }
118148 }
119149 }
120150
121151 return $ count ;
122152 }
123153
154+ protected function match (FeatureFlagSelector |array $ selector , string $ flagName , mixed $ flagValue , array $ flagList , bool $ invoke , mixed ...$ args ): int
155+ {
156+ if (is_array ($ selector )) {
157+ return $ this ->matchSelectorArray ($ selector , $ flagName , $ flagValue , $ flagList , $ invoke , ...$ args );
158+ }
159+
160+ return $ this ->matchSelector ($ selector , $ flagName , $ flagValue , $ invoke , ...$ args );
161+ }
162+
163+ protected function matchSelector (FeatureFlagSelector $ selector , string $ flagName , mixed $ flagValue , bool $ invoke , mixed ...$ args ): int
164+ {
165+ if ($ selector ->isMatch ($ flagName , $ flagValue )) {
166+ if ($ invoke ) {
167+ $ selector ->invoke ($ args );
168+ }
169+ if (!$ selector ->isContinueProcessing ()) {
170+ return -1 ;
171+ }
172+ return 1 ;
173+ }
174+
175+ return 0 ;
176+ }
177+
178+ protected function matchSelectorArray (array $ selector , string $ flagName , mixed $ flagValue , array $ flagList , bool $ invoke , mixed ...$ args ): int
179+ {
180+ $ first = array_shift ($ selector )[0 ];
181+ if (!$ first ->isMatch ($ flagName , $ flagValue )) {
182+ return 0 ;
183+ }
184+ if (empty ($ flagList )) {
185+ $ subCount = $ this ->dispatchQuerySelector ($ selector , false );
186+ } else {
187+ $ subCount = $ this ->dispatchQueryingFlags ($ flagList , $ selector , false );
188+ }
189+
190+ if ($ subCount === count ($ selector )) {
191+ return $ this ->matchSelector ($ first , $ flagName , $ flagValue , $ invoke , ...$ args );
192+ }
193+
194+ return 0 ;
195+ }
196+
124197 protected function dispatchQueryCustom (...$ args ): int
125198 {
126199 $ count = 0 ;
0 commit comments