1: <?php
2: /*
3: * SimpleID
4: *
5: * Copyright (C) Kelvin Mo 2021-2025
6: *
7: * This program is free software; you can redistribute it and/or
8: * modify it under the terms of the GNU General Public
9: * License as published by the Free Software Foundation; either
10: * version 2 of the License, or (at your option) any later version.
11: *
12: * This program is distributed in the hope that it will be useful,
13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15: * General Public License for more details.
16: *
17: * You should have received a copy of the GNU General Public
18: * License along with this program; if not, write to the Free
19: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20: *
21: */
22:
23: namespace SimpleID\Base;
24:
25: use Psr\EventDispatcher\StoppableEventInterface;
26: use SimpleID\Util\Events\BaseEvent;
27: use SimpleID\Util\Events\StoppableEventTrait;
28:
29: /**
30: * An event requesting content negotiation of a route.
31: *
32: * The route can be obtained using the {@link getRoute()} method. This
33: * is typically used to store the alias for a named route under the FatFree
34: * Framework, although it can be used to store the route pattern as well.
35: *
36: * The request body to be processed can be obtained from the
37: * {@link getRequest()} method.
38: *
39: * If the listener is able to process
40: * the request, it should call the {@link stopPropagation()}
41: * method to stop further processing.
42: */
43: class RouteContentNegotiationEvent extends BaseEvent implements StoppableEventInterface {
44: use StoppableEventTrait;
45:
46: /** @var string */
47: protected $route;
48:
49: /** @var array<string, mixed> */
50: protected $request;
51:
52: /** @var string|null */
53: protected $acceptHeader;
54:
55: /**
56: * Create a RouteNegotiationEvent
57: *
58: * @param string $route the name of the route
59: * @param array<string, mixed> $request an array containing the request body
60: * @param string|null $accept the HTTP Accept header, or null if the header
61: * is not present
62: */
63: public function __construct($route, $request, $accept) {
64: $this->route = $route;
65: $this->request = $request;
66: $this->acceptHeader = $accept;
67: }
68:
69: /** @return string */
70: public function getRoute() {
71: return $this->route;
72: }
73:
74: /** @return array<string, mixed> */
75: public function getRequest() {
76: return $this->request;
77: }
78:
79: /**
80: * Returns the supplied HTTP Accept header, or an empty string if the header
81: * is not supplied.
82: *
83: * @return string
84: */
85: public function getAcceptHeader() {
86: return ($this->acceptHeader == null) ? '' : $this->acceptHeader;
87: }
88:
89: /**
90: * Negotiate a content type based on a list of content types provided by
91: * the listener.
92: *
93: * @param array<string>|string $listener_types an array or comma separated
94: * string of content types that the listener can accept
95: * @return string|false the negotiated content type, or false if a content
96: * type cannot be negotiated
97: */
98: public function negotiate($listener_types) {
99: $client_types = [];
100:
101: foreach (explode(',', str_replace(' ', '', $this->getAcceptHeader())) as $item) {
102: if (preg_match('/(.+?)(?:;q=([\d\.]+)|$)/', $item, $parts)) {
103: $client_types[$parts[1]] = isset($parts[2]) ? $parts[2] : 1.0;
104: }
105: }
106:
107: if (!$client_types) {
108: $client_types['*/*'] = 1.0;
109: } else {
110: krsort($client_types);
111: arsort($client_types);
112: }
113:
114: if (is_string($listener_types)) $listener_types = explode(',', $listener_types);
115:
116: foreach ($client_types as $client_type => $q) {
117: if ($q && $out = preg_grep('!'. str_replace('\*', '.*', preg_quote($client_type,'!')) . '!', $listener_types)) {
118: return current($out);
119: }
120: }
121: return FALSE;
122: }
123: }
124:
125: ?>