Complex classes like RequestHandlerComponent often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use RequestHandlerComponent, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 37 | class RequestHandlerComponent extends Component { |
||
| 38 | |||
| 39 | /** |
||
| 40 | * The layout that will be switched to for Ajax requests |
||
| 41 | * |
||
| 42 | * @var string |
||
| 43 | * @see RequestHandler::setAjax() |
||
| 44 | */ |
||
| 45 | public $ajaxLayout = 'ajax'; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * Determines whether or not callbacks will be fired on this component |
||
| 49 | * |
||
| 50 | * @var boolean |
||
| 51 | */ |
||
| 52 | public $enabled = true; |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Holds the reference to Controller::$request |
||
| 56 | * |
||
| 57 | * @var CakeRequest |
||
| 58 | */ |
||
| 59 | public $request; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * Holds the reference to Controller::$response |
||
| 63 | * |
||
| 64 | * @var CakeResponse |
||
| 65 | */ |
||
| 66 | public $response; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * Contains the file extension parsed out by the Router |
||
| 70 | * |
||
| 71 | * @var string |
||
| 72 | * @see Router::parseExtensions() |
||
| 73 | */ |
||
| 74 | public $ext = null; |
||
| 75 | |||
| 76 | /** |
||
| 77 | * The template to use when rendering the given content type. |
||
| 78 | * |
||
| 79 | * @var string |
||
| 80 | */ |
||
| 81 | protected $_renderType = null; |
||
| 82 | |||
| 83 | /** |
||
| 84 | * A mapping between extensions and deserializers for request bodies of that type. |
||
| 85 | * By default only JSON and XML are mapped, use RequestHandlerComponent::addInputType() |
||
| 86 | * |
||
| 87 | * @var array |
||
| 88 | */ |
||
| 89 | protected $_inputTypeMap = array( |
||
| 90 | 'json' => array('json_decode', true) |
||
| 91 | ); |
||
| 92 | |||
| 93 | /** |
||
| 94 | * A mapping between type and viewClass |
||
| 95 | * By default only JSON and XML are mapped, use RequestHandlerComponent::viewClassMap() |
||
| 96 | * |
||
| 97 | * @var array |
||
| 98 | */ |
||
| 99 | protected $_viewClassMap = array( |
||
| 100 | 'json' => 'Json', |
||
| 101 | 'xml' => 'Xml' |
||
| 102 | ); |
||
| 103 | |||
| 104 | /** |
||
| 105 | * Constructor. Parses the accepted content types accepted by the client using HTTP_ACCEPT |
||
| 106 | * |
||
| 107 | * @param ComponentCollection $collection ComponentCollection object. |
||
| 108 | * @param array $settings Array of settings. |
||
| 109 | */ |
||
| 110 | public function __construct(ComponentCollection $collection, $settings = array()) { |
||
| 118 | |||
| 119 | /** |
||
| 120 | * Checks to see if a file extension has been parsed by the Router, or if the |
||
| 121 | * HTTP_ACCEPT_TYPE has matches only one content type with the supported extensions. |
||
| 122 | * If there is only one matching type between the supported content types & extensions, |
||
| 123 | * and the requested mime-types, RequestHandler::$ext is set to that value. |
||
| 124 | * |
||
| 125 | * @param Controller $controller A reference to the controller |
||
| 126 | * @return void |
||
| 127 | * @see Router::parseExtensions() |
||
| 128 | */ |
||
| 129 | public function initialize(Controller $controller) { |
||
| 141 | |||
| 142 | /** |
||
| 143 | * Set the extension based on the accept headers. |
||
| 144 | * Compares the accepted types and configured extensions. |
||
| 145 | * If there is one common type, that is assigned as the ext/content type |
||
| 146 | * for the response. |
||
| 147 | * Type with the highest weight will be set. If the highest weight has more |
||
| 148 | * then one type matching the extensions, the order in which extensions are specified |
||
| 149 | * determines which type will be set. |
||
| 150 | * |
||
| 151 | * If html is one of the preferred types, no content type will be set, this |
||
| 152 | * is to avoid issues with browsers that prefer html and several other content types. |
||
| 153 | * |
||
| 154 | * @return void |
||
| 155 | */ |
||
| 156 | protected function _setExtension() { |
||
| 177 | |||
| 178 | /** |
||
| 179 | * The startup method of the RequestHandler enables several automatic behaviors |
||
| 180 | * related to the detection of certain properties of the HTTP request, including: |
||
| 181 | * |
||
| 182 | * - Disabling layout rendering for Ajax requests (based on the HTTP_X_REQUESTED_WITH header) |
||
| 183 | * - If Router::parseExtensions() is enabled, the layout and template type are |
||
| 184 | * switched based on the parsed extension or Accept-Type header. For example, if `controller/action.xml` |
||
| 185 | * is requested, the view path becomes `app/View/Controller/xml/action.ctp`. Also if |
||
| 186 | * `controller/action` is requested with `Accept-Type: application/xml` in the headers |
||
| 187 | * the view path will become `app/View/Controller/xml/action.ctp`. Layout and template |
||
| 188 | * types will only switch to mime-types recognized by CakeResponse. If you need to declare |
||
| 189 | * additional mime-types, you can do so using CakeResponse::type() in your controllers beforeFilter() |
||
| 190 | * method. |
||
| 191 | * - If a helper with the same name as the extension exists, it is added to the controller. |
||
| 192 | * - If the extension is of a type that RequestHandler understands, it will set that |
||
| 193 | * Content-type in the response header. |
||
| 194 | * - If the XML data is POSTed, the data is parsed into an XML object, which is assigned |
||
| 195 | * to the $data property of the controller, which can then be saved to a model object. |
||
| 196 | * |
||
| 197 | * @param Controller $controller A reference to the controller |
||
| 198 | * @return void |
||
| 199 | */ |
||
| 200 | public function startup(Controller $controller) { |
||
| 222 | |||
| 223 | /** |
||
| 224 | * Helper method to parse xml input data, due to lack of anonymous functions |
||
| 225 | * this lives here. |
||
| 226 | * |
||
| 227 | * @param string $xml |
||
| 228 | * @return array Xml array data |
||
| 229 | */ |
||
| 230 | public function convertXml($xml) { |
||
| 241 | |||
| 242 | /** |
||
| 243 | * Handles (fakes) redirects for Ajax requests using requestAction() |
||
| 244 | * Modifies the $_POST and $_SERVER['REQUEST_METHOD'] to simulate a new GET request. |
||
| 245 | * |
||
| 246 | * @param Controller $controller A reference to the controller |
||
| 247 | * @param string|array $url A string or array containing the redirect location |
||
| 248 | * @param integer|array $status HTTP Status for redirect |
||
| 249 | * @param boolean $exit |
||
| 250 | * @return void |
||
| 251 | */ |
||
| 252 | public function beforeRedirect(Controller $controller, $url, $status = null, $exit = true) { |
||
| 275 | |||
| 276 | /** |
||
| 277 | * Checks if the response can be considered different according to the request |
||
| 278 | * headers, and the caching response headers. If it was not modified, then the |
||
| 279 | * render process is skipped. And the client will get a blank response with a |
||
| 280 | * "304 Not Modified" header. |
||
| 281 | * |
||
| 282 | * @params Controller $controller |
||
| 283 | * @return boolean false if the render process should be aborted |
||
| 284 | */ |
||
| 285 | public function beforeRender(Controller $controller) { |
||
| 290 | |||
| 291 | /** |
||
| 292 | * Returns true if the current HTTP request is Ajax, false otherwise |
||
| 293 | * |
||
| 294 | * @return boolean True if call is Ajax |
||
| 295 | * @deprecated use `$this->request->is('ajax')` instead. |
||
| 296 | */ |
||
| 297 | public function isAjax() { |
||
| 300 | |||
| 301 | /** |
||
| 302 | * Returns true if the current HTTP request is coming from a Flash-based client |
||
| 303 | * |
||
| 304 | * @return boolean True if call is from Flash |
||
| 305 | * @deprecated use `$this->request->is('flash')` instead. |
||
| 306 | */ |
||
| 307 | public function isFlash() { |
||
| 310 | |||
| 311 | /** |
||
| 312 | * Returns true if the current request is over HTTPS, false otherwise. |
||
| 313 | * |
||
| 314 | * @return boolean True if call is over HTTPS |
||
| 315 | * @deprecated use `$this->request->is('ssl')` instead. |
||
| 316 | */ |
||
| 317 | public function isSSL() { |
||
| 320 | |||
| 321 | /** |
||
| 322 | * Returns true if the current call accepts an XML response, false otherwise |
||
| 323 | * |
||
| 324 | * @return boolean True if client accepts an XML response |
||
| 325 | */ |
||
| 326 | public function isXml() { |
||
| 329 | |||
| 330 | /** |
||
| 331 | * Returns true if the current call accepts an RSS response, false otherwise |
||
| 332 | * |
||
| 333 | * @return boolean True if client accepts an RSS response |
||
| 334 | */ |
||
| 335 | public function isRss() { |
||
| 338 | |||
| 339 | /** |
||
| 340 | * Returns true if the current call accepts an Atom response, false otherwise |
||
| 341 | * |
||
| 342 | * @return boolean True if client accepts an RSS response |
||
| 343 | */ |
||
| 344 | public function isAtom() { |
||
| 347 | |||
| 348 | /** |
||
| 349 | * Returns true if user agent string matches a mobile web browser, or if the |
||
| 350 | * client accepts WAP content. |
||
| 351 | * |
||
| 352 | * @return boolean True if user agent is a mobile web browser |
||
| 353 | */ |
||
| 354 | public function isMobile() { |
||
| 357 | |||
| 358 | /** |
||
| 359 | * Returns true if the client accepts WAP content |
||
| 360 | * |
||
| 361 | * @return boolean |
||
| 362 | */ |
||
| 363 | public function isWap() { |
||
| 366 | |||
| 367 | /** |
||
| 368 | * Returns true if the current call a POST request |
||
| 369 | * |
||
| 370 | * @return boolean True if call is a POST |
||
| 371 | * @deprecated Use $this->request->is('post'); from your controller. |
||
| 372 | */ |
||
| 373 | public function isPost() { |
||
| 376 | |||
| 377 | /** |
||
| 378 | * Returns true if the current call a PUT request |
||
| 379 | * |
||
| 380 | * @return boolean True if call is a PUT |
||
| 381 | * @deprecated Use $this->request->is('put'); from your controller. |
||
| 382 | */ |
||
| 383 | public function isPut() { |
||
| 386 | |||
| 387 | /** |
||
| 388 | * Returns true if the current call a GET request |
||
| 389 | * |
||
| 390 | * @return boolean True if call is a GET |
||
| 391 | * @deprecated Use $this->request->is('get'); from your controller. |
||
| 392 | */ |
||
| 393 | public function isGet() { |
||
| 396 | |||
| 397 | /** |
||
| 398 | * Returns true if the current call a DELETE request |
||
| 399 | * |
||
| 400 | * @return boolean True if call is a DELETE |
||
| 401 | * @deprecated Use $this->request->is('delete'); from your controller. |
||
| 402 | */ |
||
| 403 | public function isDelete() { |
||
| 406 | |||
| 407 | /** |
||
| 408 | * Gets Prototype version if call is Ajax, otherwise empty string. |
||
| 409 | * The Prototype library sets a special "Prototype version" HTTP header. |
||
| 410 | * |
||
| 411 | * @return string|boolean When Ajax the prototype version of component making the call otherwise false |
||
| 412 | */ |
||
| 413 | public function getAjaxVersion() { |
||
| 417 | |||
| 418 | /** |
||
| 419 | * Adds/sets the Content-type(s) for the given name. This method allows |
||
| 420 | * content-types to be mapped to friendly aliases (or extensions), which allows |
||
| 421 | * RequestHandler to automatically respond to requests of that type in the |
||
| 422 | * startup method. |
||
| 423 | * |
||
| 424 | * @param string $name The name of the Content-type, i.e. "html", "xml", "css" |
||
| 425 | * @param string|array $type The Content-type or array of Content-types assigned to the name, |
||
| 426 | * i.e. "text/html", or "application/xml" |
||
| 427 | * @return void |
||
| 428 | * @deprecated use `$this->response->type()` instead. |
||
| 429 | */ |
||
| 430 | public function setContent($name, $type = null) { |
||
| 433 | |||
| 434 | /** |
||
| 435 | * Gets the server name from which this request was referred |
||
| 436 | * |
||
| 437 | * @return string Server address |
||
| 438 | * @deprecated use $this->request->referer() from your controller instead |
||
| 439 | */ |
||
| 440 | public function getReferer() { |
||
| 443 | |||
| 444 | /** |
||
| 445 | * Gets remote client IP |
||
| 446 | * |
||
| 447 | * @param boolean $safe |
||
| 448 | * @return string Client IP address |
||
| 449 | * @deprecated use $this->request->clientIp() from your, controller instead. |
||
| 450 | */ |
||
| 451 | public function getClientIP($safe = true) { |
||
| 454 | |||
| 455 | /** |
||
| 456 | * Determines which content types the client accepts. Acceptance is based on |
||
| 457 | * the file extension parsed by the Router (if present), and by the HTTP_ACCEPT |
||
| 458 | * header. Unlike CakeRequest::accepts() this method deals entirely with mapped content types. |
||
| 459 | * |
||
| 460 | * Usage: |
||
| 461 | * |
||
| 462 | * `$this->RequestHandler->accepts(array('xml', 'html', 'json'));` |
||
| 463 | * |
||
| 464 | * Returns true if the client accepts any of the supplied types. |
||
| 465 | * |
||
| 466 | * `$this->RequestHandler->accepts('xml');` |
||
| 467 | * |
||
| 468 | * Returns true if the client accepts xml. |
||
| 469 | * |
||
| 470 | * @param string|array $type Can be null (or no parameter), a string type name, or an |
||
| 471 | * array of types |
||
| 472 | * @return mixed If null or no parameter is passed, returns an array of content |
||
| 473 | * types the client accepts. If a string is passed, returns true |
||
| 474 | * if the client accepts it. If an array is passed, returns true |
||
| 475 | * if the client accepts one or more elements in the array. |
||
| 476 | * @see RequestHandlerComponent::setContent() |
||
| 477 | */ |
||
| 478 | public function accepts($type = null) { |
||
| 498 | |||
| 499 | /** |
||
| 500 | * Determines the content type of the data the client has sent (i.e. in a POST request) |
||
| 501 | * |
||
| 502 | * @param string|array $type Can be null (or no parameter), a string type name, or an array of types |
||
| 503 | * @return mixed If a single type is supplied a boolean will be returned. If no type is provided |
||
| 504 | * The mapped value of CONTENT_TYPE will be returned. If an array is supplied the first type |
||
| 505 | * in the request content type will be returned. |
||
| 506 | */ |
||
| 507 | public function requestedWith($type = null) { |
||
| 531 | |||
| 532 | /** |
||
| 533 | * Determines which content-types the client prefers. If no parameters are given, |
||
| 534 | * the single content-type that the client most likely prefers is returned. If $type is |
||
| 535 | * an array, the first item in the array that the client accepts is returned. |
||
| 536 | * Preference is determined primarily by the file extension parsed by the Router |
||
| 537 | * if provided, and secondarily by the list of content-types provided in |
||
| 538 | * HTTP_ACCEPT. |
||
| 539 | * |
||
| 540 | * @param string|array $type An optional array of 'friendly' content-type names, i.e. |
||
| 541 | * 'html', 'xml', 'js', etc. |
||
| 542 | * @return mixed If $type is null or not provided, the first content-type in the |
||
| 543 | * list, based on preference, is returned. If a single type is provided |
||
| 544 | * a boolean will be returned if that type is preferred. |
||
| 545 | * If an array of types are provided then the first preferred type is returned. |
||
| 546 | * If no type is provided the first preferred type is returned. |
||
| 547 | * @see RequestHandlerComponent::setContent() |
||
| 548 | */ |
||
| 549 | public function prefers($type = null) { |
||
| 579 | |||
| 580 | /** |
||
| 581 | * Sets the layout and template paths for the content type defined by $type. |
||
| 582 | * |
||
| 583 | * ### Usage: |
||
| 584 | * |
||
| 585 | * Render the response as an 'ajax' response. |
||
| 586 | * |
||
| 587 | * `$this->RequestHandler->renderAs($this, 'ajax');` |
||
| 588 | * |
||
| 589 | * Render the response as an xml file and force the result as a file download. |
||
| 590 | * |
||
| 591 | * `$this->RequestHandler->renderAs($this, 'xml', array('attachment' => 'myfile.xml');` |
||
| 592 | * |
||
| 593 | * @param Controller $controller A reference to a controller object |
||
| 594 | * @param string $type Type of response to send (e.g: 'ajax') |
||
| 595 | * @param array $options Array of options to use |
||
| 596 | * @return void |
||
| 597 | * @see RequestHandlerComponent::setContent() |
||
| 598 | * @see RequestHandlerComponent::respondAs() |
||
| 599 | */ |
||
| 600 | public function renderAs(Controller $controller, $type, $options = array()) { |
||
| 653 | |||
| 654 | /** |
||
| 655 | * Sets the response header based on type map index name. This wraps several methods |
||
| 656 | * available on CakeResponse. It also allows you to use Content-Type aliases. |
||
| 657 | * |
||
| 658 | * @param string|array $type Friendly type name, i.e. 'html' or 'xml', or a full content-type, |
||
| 659 | * like 'application/x-shockwave'. |
||
| 660 | * @param array $options If $type is a friendly type name that is associated with |
||
| 661 | * more than one type of content, $index is used to select which content-type to use. |
||
| 662 | * @return boolean Returns false if the friendly type name given in $type does |
||
| 663 | * not exist in the type map, or if the Content-type header has |
||
| 664 | * already been set by this method. |
||
| 665 | * @see RequestHandlerComponent::setContent() |
||
| 666 | */ |
||
| 667 | public function respondAs($type, $options = array()) { |
||
| 701 | |||
| 702 | /** |
||
| 703 | * Returns the current response type (Content-type header), or null if not alias exists |
||
| 704 | * |
||
| 705 | * @return mixed A string content type alias, or raw content type if no alias map exists, |
||
| 706 | * otherwise null |
||
| 707 | */ |
||
| 708 | public function responseType() { |
||
| 711 | |||
| 712 | /** |
||
| 713 | * Maps a content-type back to an alias |
||
| 714 | * |
||
| 715 | * @param string|array $cType Either a string content type to map, or an array of types. |
||
| 716 | * @return string|array Aliases for the types provided. |
||
| 717 | * @deprecated Use $this->response->mapType() in your controller instead. |
||
| 718 | */ |
||
| 719 | public function mapType($cType) { |
||
| 722 | |||
| 723 | /** |
||
| 724 | * Maps a content type alias back to its mime-type(s) |
||
| 725 | * |
||
| 726 | * @param string|array $alias String alias to convert back into a content type. Or an array of aliases to map. |
||
| 727 | * @return string Null on an undefined alias. String value of the mapped alias type. If an |
||
| 728 | * alias maps to more than one content type, the first one will be returned. |
||
| 729 | */ |
||
| 730 | public function mapAlias($alias) { |
||
| 743 | |||
| 744 | /** |
||
| 745 | * Add a new mapped input type. Mapped input types are automatically |
||
| 746 | * converted by RequestHandlerComponent during the startup() callback. |
||
| 747 | * |
||
| 748 | * @param string $type The type alias being converted, ie. json |
||
| 749 | * @param array $handler The handler array for the type. The first index should |
||
| 750 | * be the handling callback, all other arguments should be additional parameters |
||
| 751 | * for the handler. |
||
| 752 | * @return void |
||
| 753 | * @throws CakeException |
||
| 754 | */ |
||
| 755 | public function addInputType($type, $handler) { |
||
| 761 | |||
| 762 | /** |
||
| 763 | * Getter/setter for viewClassMap |
||
| 764 | * |
||
| 765 | * @param array|string $type The type string or array with format `array('type' => 'viewClass')` to map one or more |
||
| 766 | * @param array $viewClass The viewClass to be used for the type without `View` appended |
||
| 767 | * @return array|string Returns viewClass when only string $type is set, else array with viewClassMap |
||
| 768 | */ |
||
| 769 | public function viewClassMap($type = null, $viewClass = null) { |
||
| 782 | |||
| 783 | } |
||
| 784 |
Since your code implements the magic setter
_set, this function will be called for any write access on an undefined variable. You can add the@propertyannotation to your class or interface to document the existence of this variable.Since the property has write access only, you can use the @property-write annotation instead.
Of course, you may also just have mistyped another name, in which case you should fix the error.
See also the PhpDoc documentation for @property.