This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * This file is part of the Drest package. |
||
4 | * |
||
5 | * For the full copyright and license information, please view the LICENSE |
||
6 | * file that was distributed with this source code. |
||
7 | * |
||
8 | * @author Lee Davis |
||
9 | * @copyright Copyright (c) Lee Davis <@leedavis81> |
||
10 | * @link https://github.com/leedavis81/drest/blob/master/LICENSE |
||
11 | * @license http://opensource.org/licenses/MIT The MIT X License (MIT) |
||
12 | */ |
||
13 | namespace Drest\Manager; |
||
14 | |||
15 | use Drest\EntityManagerRegistry; |
||
16 | use Drest\Mapping\RouteMetaData; |
||
17 | use Drest\Configuration; |
||
18 | use Drest\Query\ExposeFields; |
||
19 | use DrestCommon\Representation\RepresentationException; |
||
20 | use DrestCommon\Representation\UnableToMatchRepresentationException; |
||
21 | use DrestCommon\Representation\AbstractRepresentation; |
||
22 | use DrestCommon\Request\Request; |
||
23 | |||
24 | class Representation |
||
25 | { |
||
26 | |||
27 | /** |
||
28 | * Drest configuration object - referenced to same instance used in Manager |
||
29 | * @var Configuration $config |
||
30 | */ |
||
31 | protected $config; |
||
32 | |||
33 | /** |
||
34 | * A request instance for inspection |
||
35 | * Reset on each getDeterminedRepresentation() |
||
36 | * @var Request $request |
||
37 | */ |
||
38 | protected $request; |
||
39 | |||
40 | /** |
||
41 | * Doctrine Entity Manager Registry |
||
42 | * @var EntityManagerRegistry $emr |
||
43 | */ |
||
44 | protected $emr; |
||
45 | |||
46 | /** |
||
47 | * @param Configuration $config |
||
48 | */ |
||
49 | 31 | public function __construct(Configuration &$config) |
|
50 | { |
||
51 | 31 | $this->config = &$config; |
|
52 | 31 | } |
|
53 | |||
54 | /** |
||
55 | * Static call to create a representation instance |
||
56 | * @param Configuration $config |
||
57 | * @return Representation |
||
58 | */ |
||
59 | 31 | public static function create(Configuration &$config) |
|
60 | { |
||
61 | 31 | return new self($config); |
|
62 | } |
||
63 | |||
64 | /** |
||
65 | * @param Request $request |
||
66 | * @param RouteMetaData $route |
||
67 | * @param EntityManagerRegistry $emr |
||
68 | * @return AbstractRepresentation |
||
69 | */ |
||
70 | 25 | public function handleExposureSettingsFromHttpMethod($request, $route, EntityManagerRegistry $emr) |
|
71 | { |
||
72 | 25 | $this->emr = $emr; |
|
73 | 25 | $this->request = $request; |
|
74 | |||
75 | 25 | $representation = $this->getDeterminedRepresentation($request, $route); |
|
76 | |||
77 | // If expose setting lookup isn't disabled, determine it |
||
78 | 25 | if (!$route->isExposeDisabled()) |
|
0 ignored issues
–
show
|
|||
79 | 25 | { |
|
80 | 25 | switch ($request->getHttpMethod()) |
|
81 | { |
||
82 | // Match on content option |
||
83 | 25 | case Request::METHOD_GET: |
|
84 | 18 | $this->handlePullExposureConfiguration($route); |
|
85 | 18 | break; |
|
86 | // Match on content-type |
||
87 | 7 | case Request::METHOD_POST: |
|
88 | 7 | case Request::METHOD_PUT: |
|
89 | 7 | case Request::METHOD_PATCH: |
|
90 | 3 | $representation = $this->handlePushExposureConfiguration($route, $representation); |
|
91 | 3 | break; |
|
92 | 25 | } |
|
93 | 25 | } |
|
94 | |||
95 | 25 | return $representation; |
|
96 | } |
||
97 | |||
98 | /** |
||
99 | * Detect an instance of a representation class using a matched route, or default representation classes |
||
100 | * @param Request $request |
||
101 | * @param RouteMetaData $route |
||
102 | * @throws UnableToMatchRepresentationException |
||
103 | * @throws RepresentationException - if unable to instantiate a representation object from config settings |
||
104 | * @return AbstractRepresentation $representation |
||
105 | */ |
||
106 | 26 | public function getDeterminedRepresentation(Request $request, RouteMetaData &$route = null) |
|
107 | { |
||
108 | 26 | $this->request = $request; |
|
109 | |||
110 | 26 | if (($representations = $this->getRepresentationClasses($route)) === []) { |
|
111 | $name = (is_null($route)) ? '"unknown name"' : $route->getName(); |
||
112 | $className = (is_null($route)) ? '"unknown class"' : $route->getClassMetaData()->getClassName(); |
||
113 | throw RepresentationException::noRepresentationsSetForRoute( |
||
114 | $name, |
||
115 | $className |
||
116 | ); |
||
117 | } |
||
118 | |||
119 | 26 | if (($representation = $this->searchAndValidateRepresentations($representations)) !== null) { |
|
120 | 26 | return $representation; |
|
121 | } |
||
122 | |||
123 | // We have no representation instances from either annotations or config object |
||
124 | throw UnableToMatchRepresentationException::noMatch(); |
||
125 | } |
||
126 | |||
127 | |||
128 | /** |
||
129 | * Handle a pull requests' exposure configuration (GET) |
||
130 | * @param RouteMetaData $route (referenced object) |
||
131 | */ |
||
132 | 18 | protected function handlePullExposureConfiguration(RouteMetaData &$route) |
|
133 | { |
||
134 | 18 | $route->setExpose( |
|
135 | 18 | ExposeFields::create($route) |
|
136 | 18 | ->configureExposeDepth( |
|
137 | 18 | $this->emr, |
|
138 | 18 | $this->config->getExposureDepth(), |
|
139 | 18 | $this->config->getExposureRelationsFetchType() |
|
140 | 18 | ) |
|
141 | 18 | ->configurePullRequest($this->config->getExposeRequestOptions(), $this->request) |
|
142 | 18 | ->toArray() |
|
143 | 18 | ); |
|
144 | 18 | } |
|
145 | |||
146 | /** |
||
147 | * Handle a push requests' exposure configuration (POST/PUT/PATCH) |
||
148 | * @param RouteMetaData $route - the matched route |
||
149 | * @param AbstractRepresentation $representation - the representation class to be used |
||
150 | * @return AbstractRepresentation $representation |
||
151 | */ |
||
152 | 3 | protected function handlePushExposureConfiguration(RouteMetaData $route, AbstractRepresentation $representation) |
|
153 | { |
||
154 | 3 | $representation = $representation::createFromString($this->request->getBody()); |
|
155 | // Write the filtered expose data |
||
156 | 3 | $representation->write( |
|
157 | 3 | ExposeFields::create($route) |
|
158 | 3 | ->configureExposeDepth( |
|
159 | 3 | $this->emr, |
|
160 | 3 | $this->config->getExposureDepth(), |
|
161 | 3 | $this->config->getExposureRelationsFetchType() |
|
162 | 3 | ) |
|
163 | 3 | ->configurePushRequest($representation->toArray()) |
|
164 | 3 | ); |
|
165 | |||
166 | 3 | return $representation; |
|
167 | } |
||
168 | |||
169 | /** |
||
170 | * Get representation options. Determined from route or config |
||
171 | * @param RouteMetaData|null $route |
||
172 | * @return array |
||
173 | */ |
||
174 | 26 | protected function getRepresentationClasses(RouteMetaData &$route = null) |
|
175 | { |
||
176 | 26 | return (is_null($route) || [] === $route->getClassMetaData()->getRepresentations()) |
|
177 | 26 | ? $this->config->getDefaultRepresentations() |
|
178 | 26 | : $route->getClassMetaData()->getRepresentations(); |
|
179 | } |
||
180 | |||
181 | |||
182 | /** |
||
183 | * Iterate through an array of representations and return a match |
||
184 | * @param array $representations |
||
185 | * @return AbstractRepresentation|null |
||
186 | * @throws RepresentationException |
||
187 | * @throws UnableToMatchRepresentationException |
||
188 | */ |
||
189 | 26 | protected function searchAndValidateRepresentations(array $representations) |
|
190 | { |
||
191 | 26 | $representationObjects = []; |
|
192 | 26 | foreach ($representations as $representation) { |
|
193 | 26 | if (($representationObj = $this->matchRepresentation($representation, $representationObjects)) instanceof AbstractRepresentation) |
|
194 | 26 | { |
|
195 | 17 | return $representationObj; |
|
196 | } |
||
197 | 22 | } |
|
198 | |||
199 | // For get requests with "415 for no media match" set on, throw an exception |
||
200 | 9 | if ($this->request->getHttpMethod() == Request::METHOD_GET && $this->config->get415ForNoMediaMatchSetting()) { |
|
201 | throw UnableToMatchRepresentationException::noMatch(); |
||
202 | } |
||
203 | |||
204 | // Return the first instantiated representation instance |
||
205 | 9 | if (isset($representationObjects[0])) { |
|
206 | 9 | return $representationObjects[0]; |
|
207 | } |
||
208 | |||
209 | return null; |
||
210 | } |
||
211 | |||
212 | |||
213 | /** |
||
214 | * Attempt to match a representation |
||
215 | * |
||
216 | * @param AbstractRepresentation|string $representation |
||
217 | * @param array $representationObjects |
||
218 | * @return AbstractRepresentation|null |
||
219 | * @throws RepresentationException |
||
220 | */ |
||
221 | 26 | protected function matchRepresentation($representation, array &$representationObjects) |
|
222 | { |
||
223 | 26 | if (!is_object($representation)) { |
|
224 | 26 | $className = $this->getRepresentationClassName($representation); |
|
225 | 26 | $representationObjects[] = $representation = new $className(); |
|
226 | 26 | } |
|
227 | 26 | if (!$representation instanceof AbstractRepresentation) { |
|
228 | throw RepresentationException::representationMustBeInstanceOfDrestRepresentation(); |
||
229 | } |
||
230 | |||
231 | 26 | if (($representation = $this->determineRepresentationByHttpMethod($representation, $this->config->getDetectContentOptions())) !== null) |
|
232 | 26 | { |
|
233 | 17 | return $representation; |
|
234 | } |
||
235 | 22 | return null; |
|
236 | } |
||
237 | |||
238 | /** |
||
239 | * Determine the representation by inspecting the HTTP method |
||
240 | * @param AbstractRepresentation $representation |
||
241 | * @param array $detectContentOptions - Eg array(self::DETECT_CONTENT_HEADER => 'Accept') |
||
242 | * @return AbstractRepresentation|null |
||
243 | */ |
||
244 | 26 | protected function determineRepresentationByHttpMethod(AbstractRepresentation $representation, array $detectContentOptions = []) |
|
245 | { |
||
246 | 26 | switch ($this->request->getHttpMethod()) { |
|
247 | // Match on content option |
||
248 | 26 | case Request::METHOD_GET: |
|
249 | // This representation matches the required media type requested by the client |
||
250 | 19 | if ($representation->isExpectedContent($detectContentOptions, $this->request)) { |
|
251 | 16 | return $representation; |
|
252 | } |
||
253 | 16 | break; |
|
254 | // Match on content-type |
||
255 | 7 | case Request::METHOD_POST: |
|
256 | 7 | case Request::METHOD_PUT: |
|
257 | 7 | case Request::METHOD_PATCH: |
|
258 | 3 | if ($representation->getContentType() === $this->request->getHeaders('Content-Type')) { |
|
259 | 1 | return $representation; |
|
260 | } |
||
261 | 2 | break; |
|
262 | 22 | } |
|
263 | 22 | return null; |
|
264 | } |
||
265 | |||
266 | |||
267 | /** |
||
268 | * Get's the representation class name. |
||
269 | * Removes any root NS chars |
||
270 | * Falls back to a DrestCommon Representation lookup |
||
271 | * |
||
272 | * @param string $representation |
||
273 | * @return string |
||
274 | * @throws RepresentationException |
||
275 | */ |
||
276 | 26 | protected function getRepresentationClassName($representation) |
|
277 | { |
||
278 | 26 | $className = (strstr($representation, '\\') !== false) |
|
279 | 26 | ? '\\' . ltrim($representation, '\\') |
|
280 | 26 | : $representation; |
|
281 | 26 | $className = (!class_exists($className)) |
|
282 | 26 | ? '\\DrestCommon\\Representation\\' . ltrim($className, '\\') |
|
283 | 26 | : $className; |
|
284 | 26 | if (!class_exists($className)) { |
|
285 | throw RepresentationException::unknownRepresentationClass($representation); |
||
286 | } |
||
287 | 26 | return $className; |
|
288 | } |
||
289 | } |
If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe: