leedavis81 /
drest
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: