[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE 8 */ 9 10 namespace Joomla\CMS\Application; 11 12 use Joomla\Application\Web\WebClient; 13 use Joomla\CMS\Access\Exception\AuthenticationFailed; 14 use Joomla\CMS\Component\ComponentHelper; 15 use Joomla\CMS\Factory; 16 use Joomla\CMS\Plugin\PluginHelper; 17 use Joomla\CMS\Router\ApiRouter; 18 use Joomla\CMS\Router\Exception\RouteNotFoundException; 19 use Joomla\CMS\Uri\Uri; 20 use Joomla\DI\Container; 21 use Joomla\Input\Json as JInputJson; 22 use Joomla\Registry\Registry; 23 use Negotiation\Accept; 24 use Negotiation\Exception\InvalidArgument; 25 use Negotiation\Negotiator; 26 27 // phpcs:disable PSR1.Files.SideEffects 28 \defined('JPATH_PLATFORM') or die; 29 // phpcs:enable PSR1.Files.SideEffects 30 31 /** 32 * Joomla! API Application class 33 * 34 * @since 4.0.0 35 */ 36 final class ApiApplication extends CMSApplication 37 { 38 /** 39 * Maps extension types to their 40 * 41 * @var array 42 * @since 4.0.0 43 */ 44 protected $formatMapper = array(); 45 46 /** 47 * The authentication plugin type 48 * 49 * @var string 50 * @since 4.0.0 51 */ 52 protected $authenticationPluginType = 'api-authentication'; 53 54 /** 55 * Class constructor. 56 * 57 * @param JInputJson $input An optional argument to provide dependency injection for the application's input 58 * object. If the argument is a JInput object that object will become the 59 * application's input object, otherwise a default input object is created. 60 * @param Registry $config An optional argument to provide dependency injection for the application's config 61 * object. If the argument is a Registry object that object will become the 62 * application's config object, otherwise a default config object is created. 63 * @param WebClient $client An optional argument to provide dependency injection for the application's client 64 * object. If the argument is a WebClient object that object will become the 65 * application's client object, otherwise a default client object is created. 66 * @param Container $container Dependency injection container. 67 * 68 * @since 4.0.0 69 */ 70 public function __construct(JInputJson $input = null, Registry $config = null, WebClient $client = null, Container $container = null) 71 { 72 // Register the application name 73 $this->name = 'api'; 74 75 // Register the client ID 76 $this->clientId = 3; 77 78 // Execute the parent constructor 79 parent::__construct($input, $config, $client, $container); 80 81 $this->addFormatMap('application/json', 'json'); 82 $this->addFormatMap('application/vnd.api+json', 'jsonapi'); 83 84 // Set the root in the URI based on the application name 85 Uri::root(null, str_ireplace('/' . $this->getName(), '', Uri::base(true))); 86 } 87 88 89 /** 90 * Method to run the application routines. 91 * 92 * Most likely you will want to instantiate a controller and execute it, or perform some sort of task directly. 93 * 94 * @return void 95 * 96 * @since 4.0.0 97 */ 98 protected function doExecute() 99 { 100 // Initialise the application 101 $this->initialiseApp(); 102 103 // Mark afterInitialise in the profiler. 104 JDEBUG ? $this->profiler->mark('afterInitialise') : null; 105 106 // Route the application 107 $this->route(); 108 109 // Mark afterApiRoute in the profiler. 110 JDEBUG ? $this->profiler->mark('afterApiRoute') : null; 111 112 // Dispatch the application 113 $this->dispatch(); 114 115 // Mark afterDispatch in the profiler. 116 JDEBUG ? $this->profiler->mark('afterDispatch') : null; 117 } 118 119 /** 120 * Adds a mapping from a content type to the format stored. Note the format type cannot be overwritten. 121 * 122 * @param string $contentHeader The content header 123 * @param string $format The content type format 124 * 125 * @return void 126 * 127 * @since 4.0.0 128 */ 129 public function addFormatMap($contentHeader, $format) 130 { 131 if (!\array_key_exists($contentHeader, $this->formatMapper)) { 132 $this->formatMapper[$contentHeader] = $format; 133 } 134 } 135 136 /** 137 * Rendering is the process of pushing the document buffers into the template 138 * placeholders, retrieving data from the document and pushing it into 139 * the application response buffer. 140 * 141 * @return void 142 * 143 * @since 4.0.0 144 * 145 * @note Rendering should be overridden to get rid of the theme files. 146 */ 147 protected function render() 148 { 149 // Render the document 150 $this->setBody($this->document->render($this->allowCache())); 151 } 152 153 /** 154 * Method to send the application response to the client. All headers will be sent prior to the main application output data. 155 * 156 * @param array $options An optional argument to enable CORS. (Temporary) 157 * 158 * @return void 159 * 160 * @since 4.0.0 161 */ 162 protected function respond($options = array()) 163 { 164 // Set the Joomla! API signature 165 $this->setHeader('X-Powered-By', 'JoomlaAPI/1.0', true); 166 167 $forceCORS = (int) $this->get('cors'); 168 169 if ($forceCORS) { 170 /** 171 * Enable CORS (Cross-origin resource sharing) 172 * Obtain allowed CORS origin from Global Settings. 173 * Set to * (=all) if not set. 174 */ 175 $allowedOrigin = $this->get('cors_allow_origin', '*'); 176 $this->setHeader('Access-Control-Allow-Origin', $allowedOrigin, true); 177 $this->setHeader('Access-Control-Allow-Headers', 'Authorization'); 178 179 if ($this->input->server->getString('HTTP_ORIGIN', null) !== null) { 180 $this->setHeader('Access-Control-Allow-Origin', $this->input->server->getString('HTTP_ORIGIN'), true); 181 $this->setHeader('Access-Control-Allow-Credentials', 'true', true); 182 } 183 } 184 185 // Parent function can be overridden later on for debugging. 186 parent::respond(); 187 } 188 189 /** 190 * Gets the name of the current template. 191 * 192 * @param boolean $params True to return the template parameters 193 * 194 * @return string|\stdClass 195 * 196 * @since 4.0.0 197 */ 198 public function getTemplate($params = false) 199 { 200 // The API application should not need to use a template 201 if ($params) { 202 $template = new \stdClass(); 203 $template->template = 'system'; 204 $template->params = new Registry(); 205 $template->inheritable = 0; 206 $template->parent = ''; 207 208 return $template; 209 } 210 211 return 'system'; 212 } 213 214 /** 215 * Route the application. 216 * 217 * Routing is the process of examining the request environment to determine which 218 * component should receive the request. The component optional parameters 219 * are then set in the request object to be processed when the application is being 220 * dispatched. 221 * 222 * @return void 223 * 224 * @since 4.0.0 225 */ 226 protected function route() 227 { 228 $router = $this->getContainer()->get(ApiRouter::class); 229 230 // Trigger the onBeforeApiRoute event. 231 PluginHelper::importPlugin('webservices'); 232 $this->triggerEvent('onBeforeApiRoute', array(&$router, $this)); 233 $caught404 = false; 234 $method = $this->input->getMethod(); 235 236 try { 237 $this->handlePreflight($method, $router); 238 239 $route = $router->parseApiRoute($method); 240 } catch (RouteNotFoundException $e) { 241 $caught404 = true; 242 } 243 244 /** 245 * Now we have an API perform content negotiation to ensure we have a valid header. Assume if the route doesn't 246 * tell us otherwise it uses the plain JSON API 247 */ 248 $priorities = array('application/vnd.api+json'); 249 250 if (!$caught404 && \array_key_exists('format', $route['vars'])) { 251 $priorities = $route['vars']['format']; 252 } 253 254 $negotiator = new Negotiator(); 255 256 try { 257 $mediaType = $negotiator->getBest($this->input->server->getString('HTTP_ACCEPT'), $priorities); 258 } catch (InvalidArgument $e) { 259 $mediaType = null; 260 } 261 262 // If we can't find a match bail with a 406 - Not Acceptable 263 if ($mediaType === null) { 264 throw new Exception\NotAcceptable('Could not match accept header', 406); 265 } 266 267 /** @var $mediaType Accept */ 268 $format = $mediaType->getValue(); 269 270 if (\array_key_exists($mediaType->getValue(), $this->formatMapper)) { 271 $format = $this->formatMapper[$mediaType->getValue()]; 272 } 273 274 $this->input->set('format', $format); 275 276 if ($caught404) { 277 throw $e; 278 } 279 280 $this->input->set('option', $route['vars']['component']); 281 $this->input->set('controller', $route['controller']); 282 $this->input->set('task', $route['task']); 283 284 foreach ($route['vars'] as $key => $value) { 285 if ($key !== 'component') { 286 if ($this->input->getMethod() === 'POST') { 287 $this->input->post->set($key, $value); 288 } else { 289 $this->input->set($key, $value); 290 } 291 } 292 } 293 294 $this->triggerEvent('onAfterApiRoute', array($this)); 295 296 if (!isset($route['vars']['public']) || $route['vars']['public'] === false) { 297 if (!$this->login(array('username' => ''), array('silent' => true, 'action' => 'core.login.api'))) { 298 throw new AuthenticationFailed(); 299 } 300 } 301 } 302 303 /** 304 * Handles preflight requests. 305 * 306 * @param String $method The REST verb 307 * 308 * @param ApiRouter $router The API Routing object 309 * 310 * @return void 311 * 312 * @since 4.0.0 313 */ 314 protected function handlePreflight($method, $router) 315 { 316 /** 317 * If not an OPTIONS request or CORS is not enabled, 318 * there's nothing useful to do here. 319 */ 320 if ($method !== 'OPTIONS' || !(int) $this->get('cors')) { 321 return; 322 } 323 324 // Extract routes matching current route from all known routes. 325 $matchingRoutes = $router->getMatchingRoutes(); 326 327 // Extract exposed methods from matching routes. 328 $matchingRoutesMethods = array_unique( 329 array_reduce( 330 $matchingRoutes, 331 function ($carry, $route) { 332 return array_merge($carry, $route->getMethods()); 333 }, 334 [] 335 ) 336 ); 337 338 /** 339 * Obtain allowed CORS origin from Global Settings. 340 * Set to * (=all) if not set. 341 */ 342 $allowedOrigin = $this->get('cors_allow_origin', '*'); 343 344 /** 345 * Obtain allowed CORS headers from Global Settings. 346 * Set to sensible default if not set. 347 */ 348 $allowedHeaders = $this->get('cors_allow_headers', 'Content-Type,X-Joomla-Token'); 349 350 /** 351 * Obtain allowed CORS methods from Global Settings. 352 * Set to methods exposed by current route if not set. 353 */ 354 $allowedMethods = $this->get('cors_allow_methods', implode(',', $matchingRoutesMethods)); 355 356 // No use to go through the regular route handling hassle, 357 // so let's simply output the headers and exit. 358 $this->setHeader('status', '204'); 359 $this->setHeader('Access-Control-Allow-Origin', $allowedOrigin); 360 $this->setHeader('Access-Control-Allow-Headers', $allowedHeaders); 361 $this->setHeader('Access-Control-Allow-Methods', $allowedMethods); 362 $this->sendHeaders(); 363 364 $this->close(); 365 } 366 367 /** 368 * Returns the application Router object. 369 * 370 * @return ApiRouter 371 * 372 * @since 4.0.0 373 * @deprecated 5.0 Inject the router or load it from the dependency injection container 374 */ 375 public function getApiRouter() 376 { 377 return $this->getContainer()->get(ApiRouter::class); 378 } 379 380 /** 381 * Dispatch the application 382 * 383 * @param string $component The component which is being rendered. 384 * 385 * @return void 386 * 387 * @since 4.0.0 388 */ 389 public function dispatch($component = null) 390 { 391 // Get the component if not set. 392 if (!$component) { 393 $component = $this->input->get('option', null); 394 } 395 396 // Load the document to the API 397 $this->loadDocument(); 398 399 // Set up the params 400 $document = Factory::getDocument(); 401 402 // Register the document object with Factory 403 Factory::$document = $document; 404 405 $contents = ComponentHelper::renderComponent($component); 406 $document->setBuffer($contents, 'component'); 407 408 // Trigger the onAfterDispatch event. 409 PluginHelper::importPlugin('system'); 410 $this->triggerEvent('onAfterDispatch'); 411 } 412 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |