PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.11.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.11.0
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
← All changes | vendor/wpfluent/framework/src/WPFluent/Http/Router.php +188 -98 2.8.02.11.0 View file →
@@ -12,30 +12,12 @@
12 12 */
13 13 protected $app = null;
14 14
15 15 /**
16 - * Name for the route.
17 - * @var array
18 - */
19 - protected $name = [];
20 -
21 - /**
22 16 * Mapping of named routes.
23 17 * @var array
24 18 */
25 19 protected $namedRoutes = [];
26 -
27 - /**
28 - * Prefix for the route
29 - * @var array
30 - */
31 - protected $prefix = [];
32 -
33 - /**
34 - * Controller/Handler namespace
35 - * @var array
36 - */
37 - protected $namespace = [];
38 20
39 21 /**
40 22 * Registered routes collection
41 23 * @var array
@@ -40,23 +22,23 @@
40 22 * Registered routes collection
41 23 * @var array
42 24 */
43 25 protected $routes = [];
44 -
26 +
45 27 /**
46 - * Route policy handler to pass to the route
28 + * Attributes staged by chained calls (prefix(), name(),
29 + * withPolicy(), before(), ...) that have not been claimed
30 + * yet. The next group() or route declaration claims them.
47 31 * @var array
48 32 */
49 - protected $policyHandler = [];
33 + protected $staged = [];
50 34
51 35 /**
52 - * Route middleware to pass to the route
36 + * Effective attributes of the group whose callback is
37 + * currently executing. Empty outside any group.
53 38 * @var array
54 39 */
55 - protected $middleware = [
56 - 'before' => [],
57 - 'after' => []
58 - ];
40 + protected $context = [];
59 41
60 42 /**
61 43 * Whether routes created by this router should override existing ones.
62 44 * @var bool
@@ -63,12 +45,12 @@
63 45 */
64 46 protected $shouldOverride = false;
65 47
66 48 /**
67 - * Keep the track of number of group calls
68 - * @var integer
49 + * Weak references to groups pending execution.
50 + * @var array
69 51 */
70 - protected $groupCount = 0;
52 + protected $pendingGroups = [];
71 53
72 54 /**
73 55 * Construct the routet instance
74 56 * @param \FluentCommunity\Framework\Foundation\Application $app
@@ -75,20 +57,27 @@
75 57 */
76 58 public function __construct($app)
77 59 {
78 60 $this->app = $app;
61 + $this->staged = $this->newAttributes();
62 + $this->context = $this->newAttributes();
79 63 }
80 64
81 65 /**
82 - * Create a route group
83 - * @param array $attributes
66 + * Create a route group.
67 + *
68 + * The group captures its effective attributes (the enclosing
69 + * group's attributes merged with anything staged for it) at
70 + * creation time, so its callback resolves the same routes no
71 + * matter when it runs: at end of statement, or later from
72 + * registerRoutes() when the instance was kept alive.
73 + *
74 + * @param array|Closure $attributes
84 75 * @param Closure|null $callback
85 - * @return null
76 + * @return \FluentCommunity\Framework\Http\Group
86 77 */
87 78 public function group($attributes = [], ?Closure $callback = null)
88 79 {
89 - $this->groupCount += 1;
90 -
91 80 if ($attributes instanceof Closure) {
92 81 $callback = $attributes;
93 82 $attributes = [];
94 83 }
@@ -110,46 +99,19 @@
110 99 }
111 100
112 101 if (isset($attributes['middleware'])) {
113 102 $middleware = $attributes['middleware'];
103 +
114 104 if (isset($middleware['before'])) {
115 105 $this->middleware('before', $middleware['before']);
116 - } elseif ($middleware['after']) {
117 - $this->middleware('after', $middleware['after']);
118 106 }
119 - }
120 107
121 - // If the current group doesn't have a policy handler
122 - // but the parent group has then bring it in this group.
123 - if (!isset($this->policyHandler[$this->groupCount])) {
124 - if (isset($this->policyHandler[$this->groupCount - 1])) {
125 - if ($policyHandler = $this->policyHandler[$this->groupCount - 1]) {
126 - $this->policyHandler[] = $policyHandler;
127 - }
108 + if (isset($middleware['after'])) {
109 + $this->middleware('after', $middleware['after']);
128 110 }
129 111 }
130 112
131 - // If the current group doesn't have a before middleware
132 - // but the parent group has then bring it in this group.
133 - if (!isset($this->middleware['before'][$this->groupCount])) {
134 - if (isset($this->middleware['before'][$this->groupCount - 1])) {
135 - if ($beforeMiddleware = $this->middleware['before'][$this->groupCount - 1]) {
136 - $this->middleware['before'][] = $beforeMiddleware;
137 - }
138 - }
139 - }
140 -
141 - // If the current group doesn't have an after middleware
142 - // but the parent group has then bring it in this group.
143 - if (!isset($this->middleware['after'][$this->groupCount])) {
144 - if (isset($this->middleware['after'][$this->groupCount - 1])) {
145 - if ($afterMiddleware = $this->middleware['after'][$this->groupCount - 1]) {
146 - $this->middleware['after'][] = $afterMiddleware;
147 - }
148 - }
149 - }
150 -
151 - return new Group($this, $callback);
113 + return new Group($this, $callback, $this->resolveAttributes());
152 114 }
153 115
154 116 /**
155 117 * Set the route name
@@ -158,9 +120,9 @@
158 120 * @return self
159 121 */
160 122 public function name($name)
161 123 {
162 - $this->name[] = $name;
124 + $this->staged['name'][] = $name;
163 125
164 126 return $this;
165 127 }
166 128
@@ -171,9 +133,9 @@
171 133 * @return self
172 134 */
173 135 public function prefix($prefix)
174 136 {
175 - $this->prefix[] = $prefix;
137 + $this->staged['prefix'][] = $prefix;
176 138
177 139 return $this;
178 140 }
179 141
@@ -184,9 +146,9 @@
184 146 * @return self
185 147 */
186 148 public function namespace($ns)
187 149 {
188 - $this->namespace[] = $ns;
150 + $this->staged['namespace'][] = $ns;
189 151
190 152 return $this;
191 153 }
192 154
@@ -215,9 +177,9 @@
215 177 if (is_array($handler = $method ? func_get_args() : $handler)) {
216 178 $handler = implode('@', $handler);
217 179 }
218 180
219 - $this->policyHandler[] = $handler;
181 + $this->staged['policy'] = $handler;
220 182
221 183 return $this;
222 184 }
223 185
@@ -254,10 +216,10 @@
254 216 if (is_array($middleware[0])) {
255 217 $middleware = reset($middleware);
256 218 }
257 219
258 - $this->middleware[$type] = array_merge(
259 - $this->middleware[$type], $middleware
220 + $this->staged[$type] = array_merge(
221 + $this->staged[$type], $middleware
260 222 );
261 223
262 224 return $this;
263 225 }
@@ -262,23 +224,73 @@
262 224 return $this;
263 225 }
264 226
265 227 /**
266 - * Execute the route group callback
267 - *
228 + * Merge whatever is currently staged into the given attributes
229 + * and clear the staging area. Used by Group to absorb calls
230 + * chained after group() into its own attributes.
231 + *
232 + * @param array $attributes
233 + * @return array
234 + */
235 + public function absorbStaged(array $attributes)
236 + {
237 + return $this->mergeAttributes($attributes, $this->takeStaged());
238 + }
239 +
240 + /**
241 + * Track a group so any instance kept alive past its statement
242 + * can still be executed before routes are registered. A weak
243 + * reference keeps the destructor firing at end of statement.
244 + *
245 + * @param Group $group
246 + * @return void
247 + */
248 + public function trackGroup(Group $group)
249 + {
250 + if (class_exists(\WeakReference::class)) {
251 + $this->pendingGroups[] = \WeakReference::create($group);
252 + }
253 + }
254 +
255 + /**
256 + * Execute any groups still pending execution because a
257 + * reference to them was held beyond their statement.
258 + *
259 + * @return void
260 + */
261 + protected function executePendingGroups()
262 + {
263 + while ($this->pendingGroups) {
264 + $reference = array_shift($this->pendingGroups);
265 +
266 + if ($group = $reference->get()) {
267 + $group->execute();
268 + }
269 + }
270 + }
271 +
272 + /**
273 + * Execute a route group callback with the group's attributes
274 + * as the active context. The previous context is restored
275 + * afterwards, even if the callback throws, so execution is
276 + * safe at any nesting depth and at any time.
277 + *
268 278 * @param Closure $callback
269 - * @return null
279 + * @param array $attributes
280 + * @return void
270 281 */
271 - public function executeGroupCallback($callback)
282 + public function executeGroupCallback($callback, array $attributes = [])
272 283 {
273 - $callback($this);
274 - $this->groupCount -= 1;
275 - array_pop($this->name);
276 - array_pop($this->prefix);
277 - array_pop($this->namespace);
278 - array_pop($this->middleware['before']);
279 - array_pop($this->middleware['after']);
280 - array_pop($this->policyHandler);
284 + $previous = $this->context;
285 +
286 + $this->context = $attributes + $this->newAttributes();
287 +
288 + try {
289 + $callback($this);
290 + } finally {
291 + $this->context = $previous;
292 + }
281 293 }
282 294
283 295 /**
284 296 * Declare a GET route endpoint
@@ -378,34 +390,36 @@
378 390 * @return \FluentCommunity\Framework\Http\Route
379 391 */
380 392 protected function newRoute($uri, $handler, $method)
381 393 {
394 + $attributes = $this->resolveAttributes();
395 +
382 396 $route = Route::create(
383 397 $this->app,
384 398 $this->getRestNamespace(),
385 - $this->buildUriWithPrefix($uri),
399 + $this->buildUriWithPrefix($uri, $attributes['prefix']),
386 400 $handler,
387 401 $method
388 402 );
389 403
390 - if ($this->name) {
391 - $route->withName($this->name);
404 + if ($attributes['name']) {
405 + $route->withName($attributes['name']);
392 406 }
393 407
394 - if ($this->namespace) {
395 - $route->withNamespace($this->namespace);
408 + if ($attributes['namespace']) {
409 + $route->withNamespace($attributes['namespace']);
396 410 }
397 411
398 - if ($this->policyHandler) {
399 - $route->withPolicy(end($this->policyHandler));
412 + if ($attributes['policy']) {
413 + $route->withPolicy($attributes['policy']);
400 414 }
401 415
402 - if ($this->middleware['before']) {
403 - $route->before($this->middleware['before']);
416 + if ($attributes['before']) {
417 + $route->before($attributes['before']);
404 418 }
405 419
406 - if ($this->middleware['after']) {
407 - $route->after($this->middleware['after']);
420 + if ($attributes['after']) {
421 + $route->after($attributes['after']);
408 422 }
409 423
410 424 if ($this->shouldOverride) {
411 425 $route->override();
@@ -414,8 +428,79 @@
414 428 return $route->preparefrontendHandlers();
415 429 }
416 430
417 431 /**
432 + * An empty attribute set.
433 + *
434 + * @return array
435 + */
436 + protected function newAttributes()
437 + {
438 + return [
439 + 'name' => [],
440 + 'prefix' => [],
441 + 'namespace' => [],
442 + 'policy' => null,
443 + 'before' => [],
444 + 'after' => [],
445 + ];
446 + }
447 +
448 + /**
449 + * Return the staged attributes and reset the staging area.
450 + *
451 + * @return array
452 + */
453 + protected function takeStaged()
454 + {
455 + $staged = $this->staged;
456 +
457 + $this->staged = $this->newAttributes();
458 +
459 + return $staged;
460 + }
461 +
462 + /**
463 + * The effective attributes for a route or group declared right
464 + * now: the current group context merged with the staged
465 + * attributes, which are claimed in the process.
466 + *
467 + * @return array
468 + */
469 + protected function resolveAttributes()
470 + {
471 + return $this->mergeAttributes($this->context, $this->takeStaged());
472 + }
473 +
474 + /**
475 + * Merge attribute sets, outermost first. List attributes
476 + * accumulate; the policy of the innermost set that declares
477 + * one wins, so nested groups inherit their parent's policy
478 + * unless they declare their own.
479 + *
480 + * @param array ...$sets
481 + * @return array
482 + */
483 + protected function mergeAttributes(array ...$sets)
484 + {
485 + $merged = $this->newAttributes();
486 +
487 + foreach ($sets as $set) {
488 + foreach (['name', 'prefix', 'namespace', 'before', 'after'] as $key) {
489 + if (!empty($set[$key])) {
490 + $merged[$key] = array_merge($merged[$key], $set[$key]);
491 + }
492 + }
493 +
494 + if (isset($set['policy'])) {
495 + $merged['policy'] = $set['policy'];
496 + }
497 + }
498 +
499 + return $merged;
500 + }
501 +
502 + /**
418 503 * Resolve the rest namespace for the plugin
419 504 *
420 505 * @return string
421 506 */
@@ -433,17 +518,18 @@
433 518 /**
434 519 * Build the URI with the prefix
435 520 *
436 521 * @param string $uri
522 + * @param array $prefixes
437 523 * @return string The URI
438 524 */
439 - protected function buildUriWithPrefix($uri)
525 + protected function buildUriWithPrefix($uri, array $prefixes = [])
440 526 {
441 527 $uri = trim($uri, '/');
442 528
443 529 $prefix = array_map(function($prefix) {
444 530 return trim($prefix, '/');
445 - }, $this->prefix);
531 + }, $prefixes);
446 532
447 533 $prefix = implode('/', $prefix);
448 534
449 535 return trim($prefix, '/') . '/' . trim($uri, '/');
@@ -463,13 +549,17 @@
463 549
464 550 /**
465 551 * Register all the routse in WordPress Rest Engine
466 552 *
467 - * @return null
553 + * @return void
468 554 */
469 555 public function registerRoutes()
470 556 {
471 - foreach ($this->routes as $route) $route->register();
557 + $this->executePendingGroups();
558 +
559 + foreach ($this->getRoutes() as $route) {
560 + $route->register();
561 + }
472 562 }
473 563
474 564 /**
475 565 * Get all ther registered routes