PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/cake/libs/controller/components/request_handler.php

https://github.com/msadouni/cakephp2x
PHP | 776 lines | 389 code | 72 blank | 315 comment | 103 complexity | ea30e322070b8a11576bebaf326e1a4f MD5 | raw file
  1. <?php
  2. /**
  3. * Request object for handling alternative HTTP requests
  4. *
  5. * Alternative HTTP requests can come from wireless units like mobile phones, palmtop computers,
  6. * and the like. These units have no use for Ajax requests, and this Component can tell how Cake
  7. * should respond to the different needs of a handheld computer and a desktop machine.
  8. *
  9. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  10. * Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. *
  12. * Licensed under The MIT License
  13. * Redistributions of files must retain the above copyright notice.
  14. *
  15. * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  16. * @link http://cakephp.org CakePHP(tm) Project
  17. * @package cake
  18. * @subpackage cake.cake.libs.controller.components
  19. * @since CakePHP(tm) v 0.10.4.1076
  20. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  21. */
  22. if (!defined('REQUEST_MOBILE_UA')) {
  23. define(
  24. 'REQUEST_MOBILE_UA',
  25. '(iPhone|MIDP|AvantGo|BlackBerry|J2ME|Opera Mini|DoCoMo|NetFront|Nokia|PalmOS|PalmSource|portalmmm|Plucker|ReqwirelessWeb|SonyEricsson|Symbian|UP\.Browser|Windows CE|Xiino)'
  26. );
  27. }
  28. /**
  29. * Request object for handling HTTP requests
  30. *
  31. * @package cake
  32. * @subpackage cake.cake.libs.controller.components
  33. *
  34. */
  35. class RequestHandlerComponent extends Object {
  36. /**
  37. * The layout that will be switched to for Ajax requests
  38. *
  39. * @var string
  40. * @access public
  41. * @see RequestHandler::setAjax()
  42. */
  43. public $ajaxLayout = 'ajax';
  44. /**
  45. * Determines whether or not callbacks will be fired on this component
  46. *
  47. * @var boolean
  48. * @access public
  49. */
  50. public $enabled = true;
  51. /**
  52. * Holds the content-type of the response that is set when using
  53. * RequestHandler::respondAs()
  54. *
  55. * @var string
  56. * @access private
  57. */
  58. private $__responseTypeSet = null;
  59. /**
  60. * Holds the copy of Controller::$params
  61. *
  62. * @var array
  63. * @access public
  64. */
  65. public $params = array();
  66. /**
  67. * Friendly content-type mappings used to set response types and determine
  68. * request types. Can be modified with RequestHandler::setContent()
  69. *
  70. * @var array
  71. * @access private
  72. * @see RequestHandlerComponent::setContent
  73. */
  74. private $__requestContent = array(
  75. 'javascript' => 'text/javascript',
  76. 'js' => 'text/javascript',
  77. 'json' => 'application/json',
  78. 'css' => 'text/css',
  79. 'html' => array('text/html', '*/*'),
  80. 'text' => 'text/plain',
  81. 'txt' => 'text/plain',
  82. 'csv' => array('application/vnd.ms-excel', 'text/plain'),
  83. 'form' => 'application/x-www-form-urlencoded',
  84. 'file' => 'multipart/form-data',
  85. 'xhtml' => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'),
  86. 'xhtml-mobile' => 'application/vnd.wap.xhtml+xml',
  87. 'xml' => array('application/xml', 'text/xml'),
  88. 'rss' => 'application/rss+xml',
  89. 'atom' => 'application/atom+xml',
  90. 'amf' => 'application/x-amf',
  91. 'wap' => array(
  92. 'text/vnd.wap.wml',
  93. 'text/vnd.wap.wmlscript',
  94. 'image/vnd.wap.wbmp'
  95. ),
  96. 'wml' => 'text/vnd.wap.wml',
  97. 'wmlscript' => 'text/vnd.wap.wmlscript',
  98. 'wbmp' => 'image/vnd.wap.wbmp',
  99. 'pdf' => 'application/pdf',
  100. 'zip' => 'application/x-zip',
  101. 'tar' => 'application/x-tar'
  102. );
  103. /**
  104. * Content-types accepted by the client. If extension parsing is enabled in the
  105. * Router, and an extension is detected, the corresponding content-type will be
  106. * used as the overriding primary content-type accepted.
  107. *
  108. * @var array
  109. * @access private
  110. * @see Router::parseExtensions()
  111. */
  112. private $__acceptTypes = array();
  113. /**
  114. * The template to use when rendering the given content type.
  115. *
  116. * @var string
  117. * @access private
  118. */
  119. private $__renderType = null;
  120. /**
  121. * Contains the file extension parsed out by the Router
  122. *
  123. * @var string
  124. * @access public
  125. * @see Router::parseExtensions()
  126. */
  127. public $ext = null;
  128. /**
  129. * Flag set when MIME types have been initialized
  130. *
  131. * @var boolean
  132. * @access private
  133. * @see RequestHandler::__initializeTypes()
  134. */
  135. private $__typesInitialized = false;
  136. /**
  137. * Constructor. Parses the accepted content types accepted by the client using HTTP_ACCEPT
  138. *
  139. */
  140. public function __construct() {
  141. $this->__acceptTypes = explode(',', env('HTTP_ACCEPT'));
  142. foreach ($this->__acceptTypes as $i => $type) {
  143. if (strpos($type, ';')) {
  144. $type = explode(';', $type);
  145. $this->__acceptTypes[$i] = $type[0];
  146. }
  147. }
  148. parent::__construct();
  149. }
  150. /**
  151. * Initializes the component, gets a reference to Controller::$parameters, and
  152. * checks to see if a file extension has been parsed by the Router. If yes, the
  153. * corresponding content-type is pushed onto the list of accepted content-types
  154. * as the first item.
  155. *
  156. * @param object $controller A reference to the controller
  157. * @return void
  158. * @see Router::parseExtensions()
  159. * @access public
  160. */
  161. public function initialize(&$controller) {
  162. if (isset($controller->params['url']['ext'])) {
  163. $this->ext = $controller->params['url']['ext'];
  164. }
  165. }
  166. /**
  167. * The startup method of the RequestHandler enables several automatic behaviors
  168. * related to the detection of certain properties of the HTTP request, including:
  169. *
  170. * - Disabling layout rendering for Ajax requests (based on the HTTP_X_REQUESTED_WITH header)
  171. * - If Router::parseExtensions() is enabled, the layout and template type are
  172. * switched based on the parsed extension. For example, if controller/action.xml
  173. * is requested, the view path becomes <i>app/views/controller/xml/action.ctp</i>.
  174. * - If a helper with the same name as the extension exists, it is added to the controller.
  175. * - If the extension is of a type that RequestHandler understands, it will set that
  176. * Content-type in the response header.
  177. * - If the XML data is POSTed, the data is parsed into an XML object, which is assigned
  178. * to the $data property of the controller, which can then be saved to a model object.
  179. *
  180. * @param object $controller A reference to the controller
  181. * @return void
  182. * @access public
  183. */
  184. public function startup(&$controller) {
  185. if (!$this->enabled) {
  186. return;
  187. }
  188. $this->__initializeTypes();
  189. $controller->params['isAjax'] = $this->isAjax();
  190. $isRecognized = (
  191. !in_array($this->ext, array('html', 'htm')) &&
  192. in_array($this->ext, array_keys($this->__requestContent))
  193. );
  194. if (!empty($this->ext) && $isRecognized) {
  195. $this->renderAs($controller, $this->ext);
  196. } elseif ($this->isAjax()) {
  197. $this->renderAs($controller, 'ajax');
  198. }
  199. if ($this->requestedWith('xml')) {
  200. if (!class_exists('XmlNode')) {
  201. App::import('Core', 'Xml');
  202. }
  203. $xml = new Xml(trim(file_get_contents('php://input')));
  204. if (is_object($xml->child('data')) && count($xml->children) == 1) {
  205. $controller->data = $xml->child('data');
  206. } else {
  207. $controller->data = $xml;
  208. }
  209. }
  210. }
  211. /**
  212. * Handles (fakes) redirects for Ajax requests using requestAction()
  213. *
  214. * @param object $controller A reference to the controller
  215. * @param mixed $url A string or array containing the redirect location
  216. * @access public
  217. */
  218. public function beforeRedirect(&$controller, $url) {
  219. if (!$this->isAjax()) {
  220. return;
  221. }
  222. foreach ($_POST as $key => $val) {
  223. unset($_POST[$key]);
  224. }
  225. echo $this->requestAction($url, array('return'));
  226. $this->_stop();
  227. }
  228. /**
  229. * Returns true if the current HTTP request is Ajax, false otherwise
  230. *
  231. * @return boolean True if call is Ajax
  232. * @access public
  233. */
  234. public function isAjax() {
  235. return env('HTTP_X_REQUESTED_WITH') === "XMLHttpRequest";
  236. }
  237. /**
  238. * Returns true if the current HTTP request is coming from a Flash-based client
  239. *
  240. * @return boolean True if call is from Flash
  241. * @access public
  242. */
  243. public function isFlash() {
  244. return (preg_match('/^(Shockwave|Adobe) Flash/', env('HTTP_USER_AGENT')) == 1);
  245. }
  246. /**
  247. * Returns true if the current request is over HTTPS, false otherwise.
  248. *
  249. * @return bool True if call is over HTTPS
  250. * @access public
  251. */
  252. public function isSSL() {
  253. return env('HTTPS');
  254. }
  255. /**
  256. * Returns true if the current call accepts an XML response, false otherwise
  257. *
  258. * @return boolean True if client accepts an XML response
  259. * @access public
  260. */
  261. public function isXml() {
  262. return $this->prefers('xml');
  263. }
  264. /**
  265. * Returns true if the current call accepts an RSS response, false otherwise
  266. *
  267. * @return boolean True if client accepts an RSS response
  268. * @access public
  269. */
  270. public function isRss() {
  271. return $this->prefers('rss');
  272. }
  273. /**
  274. * Returns true if the current call accepts an Atom response, false otherwise
  275. *
  276. * @return boolean True if client accepts an RSS response
  277. * @access public
  278. */
  279. public function isAtom() {
  280. return $this->prefers('atom');
  281. }
  282. /**
  283. * Returns true if user agent string matches a mobile web browser, or if the
  284. * client accepts WAP content.
  285. *
  286. * @return boolean True if user agent is a mobile web browser
  287. * @access public
  288. */
  289. public function isMobile() {
  290. preg_match('/' . REQUEST_MOBILE_UA . '/i', env('HTTP_USER_AGENT'), $match);
  291. if (!empty($match) || $this->accepts('wap')) {
  292. return true;
  293. }
  294. return false;
  295. }
  296. /**
  297. * Returns true if the client accepts WAP content
  298. *
  299. * @return bool
  300. * @access public
  301. */
  302. public function isWap() {
  303. return $this->prefers('wap');
  304. }
  305. /**
  306. * Returns true if the current call a POST request
  307. *
  308. * @return boolean True if call is a POST
  309. * @access public
  310. */
  311. public function isPost() {
  312. return (strtolower(env('REQUEST_METHOD')) == 'post');
  313. }
  314. /**
  315. * Returns true if the current call a PUT request
  316. *
  317. * @return boolean True if call is a PUT
  318. * @access public
  319. */
  320. public function isPut() {
  321. return (strtolower(env('REQUEST_METHOD')) == 'put');
  322. }
  323. /**
  324. * Returns true if the current call a GET request
  325. *
  326. * @return boolean True if call is a GET
  327. * @access public
  328. */
  329. public function isGet() {
  330. return (strtolower(env('REQUEST_METHOD')) == 'get');
  331. }
  332. /**
  333. * Returns true if the current call a DELETE request
  334. *
  335. * @return boolean True if call is a DELETE
  336. * @access public
  337. */
  338. public function isDelete() {
  339. return (strtolower(env('REQUEST_METHOD')) == 'delete');
  340. }
  341. /**
  342. * Gets Prototype version if call is Ajax, otherwise empty string.
  343. * The Prototype library sets a special "Prototype version" HTTP header.
  344. *
  345. * @return string Prototype version of component making Ajax call
  346. * @access public
  347. */
  348. public function getAjaxVersion() {
  349. if (env('HTTP_X_PROTOTYPE_VERSION') != null) {
  350. return env('HTTP_X_PROTOTYPE_VERSION');
  351. }
  352. return false;
  353. }
  354. /**
  355. * Adds/sets the Content-type(s) for the given name. This method allows
  356. * content-types to be mapped to friendly aliases (or extensions), which allows
  357. * RequestHandler to automatically respond to requests of that type in the
  358. * startup method.
  359. *
  360. * @param string $name The name of the Content-type, i.e. "html", "xml", "css"
  361. * @param mixed $type The Content-type or array of Content-types assigned to the name,
  362. * i.e. "text/html", or "application/xml"
  363. * @return void
  364. * @access public
  365. */
  366. public function setContent($name, $type = null) {
  367. if (is_array($name)) {
  368. $this->__requestContent = array_merge($this->__requestContent, $name);
  369. return;
  370. }
  371. $this->__requestContent[$name] = $type;
  372. }
  373. /**
  374. * Gets the server name from which this request was referred
  375. *
  376. * @return string Server address
  377. * @access public
  378. */
  379. public function getReferrer() {
  380. if (env('HTTP_HOST') != null) {
  381. $sessHost = env('HTTP_HOST');
  382. }
  383. if (env('HTTP_X_FORWARDED_HOST') != null) {
  384. $sessHost = env('HTTP_X_FORWARDED_HOST');
  385. }
  386. return trim(preg_replace('/(?:\:.*)/', '', $sessHost));
  387. }
  388. /**
  389. * Gets remote client IP
  390. *
  391. * @return string Client IP address
  392. * @access public
  393. */
  394. public function getClientIP($safe = true) {
  395. if (!$safe && env('HTTP_X_FORWARDED_FOR') != null) {
  396. $ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
  397. } else {
  398. if (env('HTTP_CLIENT_IP') != null) {
  399. $ipaddr = env('HTTP_CLIENT_IP');
  400. } else {
  401. $ipaddr = env('REMOTE_ADDR');
  402. }
  403. }
  404. if (env('HTTP_CLIENTADDRESS') != null) {
  405. $tmpipaddr = env('HTTP_CLIENTADDRESS');
  406. if (!empty($tmpipaddr)) {
  407. $ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
  408. }
  409. }
  410. return trim($ipaddr);
  411. }
  412. /**
  413. * Determines which content types the client accepts. Acceptance is based on
  414. * the file extension parsed by the Router (if present), and by the HTTP_ACCEPT
  415. * header.
  416. *
  417. * @param mixed $type Can be null (or no parameter), a string type name, or an
  418. * array of types
  419. * @return mixed If null or no parameter is passed, returns an array of content
  420. * types the client accepts. If a string is passed, returns true
  421. * if the client accepts it. If an array is passed, returns true
  422. * if the client accepts one or more elements in the array.
  423. * @access public
  424. * @see RequestHandlerComponent::setContent()
  425. */
  426. public function accepts($type = null) {
  427. $this->__initializeTypes();
  428. if ($type == null) {
  429. return $this->mapType($this->__acceptTypes);
  430. } elseif (is_array($type)) {
  431. foreach ($type as $t) {
  432. if ($this->accepts($t) == true) {
  433. return true;
  434. }
  435. }
  436. return false;
  437. } elseif (is_string($type)) {
  438. if (!isset($this->__requestContent[$type])) {
  439. return false;
  440. }
  441. $content = $this->__requestContent[$type];
  442. if (is_array($content)) {
  443. foreach ($content as $c) {
  444. if (in_array($c, $this->__acceptTypes)) {
  445. return true;
  446. }
  447. }
  448. } else {
  449. if (in_array($content, $this->__acceptTypes)) {
  450. return true;
  451. }
  452. }
  453. }
  454. }
  455. /**
  456. * Determines the content type of the data the client has sent (i.e. in a POST request)
  457. *
  458. * @param mixed $type Can be null (or no parameter), a string type name, or an array of types
  459. * @return mixed
  460. * @access public
  461. */
  462. public function requestedWith($type = null) {
  463. if (!$this->isPost() && !$this->isPut()) {
  464. return null;
  465. }
  466. list($contentType) = explode(';', env('CONTENT_TYPE'));
  467. if ($type == null) {
  468. return $this->mapType($contentType);
  469. } elseif (is_array($type)) {
  470. foreach ($type as $t) {
  471. if ($this->requestedWith($t)) {
  472. return $this->mapType($t);
  473. }
  474. }
  475. return false;
  476. } elseif (is_string($type)) {
  477. return ($type == $this->mapType($contentType));
  478. }
  479. }
  480. /**
  481. * Determines which content-types the client prefers. If no parameters are given,
  482. * the content-type that the client most likely prefers is returned. If $type is
  483. * an array, the first item in the array that the client accepts is returned.
  484. * Preference is determined primarily by the file extension parsed by the Router
  485. * if provided, and secondarily by the list of content-types provided in
  486. * HTTP_ACCEPT.
  487. *
  488. * @param mixed $type An optional array of 'friendly' content-type names, i.e.
  489. * 'html', 'xml', 'js', etc.
  490. * @return mixed If $type is null or not provided, the first content-type in the
  491. * list, based on preference, is returned.
  492. * @access public
  493. * @see RequestHandlerComponent::setContent()
  494. */
  495. public function prefers($type = null) {
  496. $this->__initializeTypes();
  497. $accept = $this->accepts();
  498. if ($type == null) {
  499. if (empty($this->ext)) {
  500. if (is_array($accept)) {
  501. return $accept[0];
  502. }
  503. return $accept;
  504. }
  505. return $this->ext;
  506. }
  507. $types = $type;
  508. if (is_string($type)) {
  509. $types = array($type);
  510. }
  511. if (count($types) === 1) {
  512. if (!empty($this->ext)) {
  513. return ($types[0] == $this->ext);
  514. }
  515. return ($types[0] == $accept[0]);
  516. }
  517. $accepts = array();
  518. foreach ($types as $type) {
  519. if (in_array($type, $accept)) {
  520. $accepts[] = $type;
  521. }
  522. }
  523. if (count($accepts) === 0) {
  524. return false;
  525. } elseif (count($types) === 1) {
  526. return ($types[0] === $accepts[0]);
  527. } elseif (count($accepts) === 1) {
  528. return $accepts[0];
  529. }
  530. $acceptedTypes = array();
  531. foreach ($this->__acceptTypes as $type) {
  532. $acceptedTypes[] = $this->mapType($type);
  533. }
  534. $accepts = array_intersect($acceptedTypes, $accepts);
  535. return $accepts[0];
  536. }
  537. /**
  538. * Sets the layout and template paths for the content type defined by $type.
  539. *
  540. * @param object $controller A reference to a controller object
  541. * @param string $type Type of response to send (e.g: 'ajax')
  542. * @return void
  543. * @access public
  544. * @see RequestHandlerComponent::setContent()
  545. * @see RequestHandlerComponent::respondAs()
  546. */
  547. public function renderAs(&$controller, $type) {
  548. $this->__initializeTypes();
  549. $options = array('charset' => 'UTF-8');
  550. if (Configure::read('App.encoding') !== null) {
  551. $options = array('charset' => Configure::read('App.encoding'));
  552. }
  553. if ($type == 'ajax') {
  554. $controller->layout = $this->ajaxLayout;
  555. return $this->respondAs('html', $options);
  556. }
  557. $controller->ext = '.ctp';
  558. if (empty($this->__renderType)) {
  559. $controller->viewPath .= DS . $type;
  560. } else {
  561. $remove = preg_replace("/([\/\\\\]{$this->__renderType})$/", DS . $type, $controller->viewPath);
  562. $controller->viewPath = $remove;
  563. }
  564. $this->__renderType = $type;
  565. $controller->layoutPath = $type;
  566. if (isset($this->__requestContent[$type])) {
  567. $this->respondAs($type, $options);
  568. }
  569. $helper = ucfirst($type);
  570. $isAdded = (
  571. in_array($helper, $controller->helpers) ||
  572. array_key_exists($helper, $controller->helpers)
  573. );
  574. if (!$isAdded) {
  575. if (App::import('Helper', $helper)) {
  576. $controller->helpers[] = $helper;
  577. }
  578. }
  579. }
  580. /**
  581. * Sets the response header based on type map index name. If DEBUG is greater than 2, the header
  582. * is not set.
  583. *
  584. * @param mixed $type Friendly type name, i.e. 'html' or 'xml', or a full content-type,
  585. * like 'application/x-shockwave'.
  586. * @param array $options If $type is a friendly type name that is associated with
  587. * more than one type of content, $index is used to select which content-type to use.
  588. *
  589. * @return boolean Returns false if the friendly type name given in $type does
  590. * not exist in the type map, or if the Content-type header has
  591. * already been set by this method.
  592. * @access public
  593. * @see RequestHandlerComponent::setContent()
  594. */
  595. public function respondAs($type, $options = array()) {
  596. $this->__initializeTypes();
  597. if ($this->__responseTypeSet != null) {
  598. return false;
  599. }
  600. if (!array_key_exists($type, $this->__requestContent) && strpos($type, '/') === false) {
  601. return false;
  602. }
  603. $defaults = array('index' => 0, 'charset' => null, 'attachment' => false);
  604. $options = array_merge($defaults, $options);
  605. if (strpos($type, '/') === false && isset($this->__requestContent[$type])) {
  606. $cType = null;
  607. if (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][$options['index']])) {
  608. $cType = $this->__requestContent[$type][$options['index']];
  609. } elseif (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][0])) {
  610. $cType = $this->__requestContent[$type][0];
  611. } elseif (isset($this->__requestContent[$type])) {
  612. $cType = $this->__requestContent[$type];
  613. } else {
  614. return false;
  615. }
  616. if (is_array($cType)) {
  617. if ($this->prefers($cType)) {
  618. $cType = $this->prefers($cType);
  619. } else {
  620. $cType = $cType[0];
  621. }
  622. }
  623. } else {
  624. $cType = $type;
  625. }
  626. if ($cType != null) {
  627. $header = 'Content-type: ' . $cType;
  628. if (!empty($options['charset'])) {
  629. $header .= '; charset=' . $options['charset'];
  630. }
  631. if (!empty($options['attachment'])) {
  632. header("Content-Disposition: attachment; filename=\"{$options['attachment']}\"");
  633. }
  634. if (Configure::read() < 2 && !defined('CAKEPHP_SHELL')) {
  635. @header($header);
  636. }
  637. $this->__responseTypeSet = $cType;
  638. return true;
  639. }
  640. return false;
  641. }
  642. /**
  643. * Returns the current response type (Content-type header), or null if none has been set
  644. *
  645. * @return mixed A string content type alias, or raw content type if no alias map exists,
  646. * otherwise null
  647. * @access public
  648. */
  649. public function responseType() {
  650. if ($this->__responseTypeSet == null) {
  651. return null;
  652. }
  653. return $this->mapType($this->__responseTypeSet);
  654. }
  655. /**
  656. * Maps a content-type back to an alias
  657. *
  658. * @param mixed $type Content type
  659. * @return mixed Alias
  660. * @access public
  661. */
  662. public function mapType($ctype) {
  663. if (is_array($ctype)) {
  664. $out = array();
  665. foreach ($ctype as $t) {
  666. $out[] = $this->mapType($t);
  667. }
  668. return $out;
  669. } else {
  670. $keys = array_keys($this->__requestContent);
  671. $count = count($keys);
  672. for ($i = 0; $i < $count; $i++) {
  673. $name = $keys[$i];
  674. $type = $this->__requestContent[$name];
  675. if (is_array($type) && in_array($ctype, $type)) {
  676. return $name;
  677. } elseif (!is_array($type) && $type == $ctype) {
  678. return $name;
  679. }
  680. }
  681. return $ctype;
  682. }
  683. }
  684. /**
  685. * Initializes MIME types
  686. *
  687. * @return void
  688. * @access private
  689. */
  690. private function __initializeTypes() {
  691. if ($this->__typesInitialized) {
  692. return;
  693. }
  694. if (isset($this->__requestContent[$this->ext])) {
  695. $content = $this->__requestContent[$this->ext];
  696. if (is_array($content)) {
  697. $content = $content[0];
  698. }
  699. array_unshift($this->__acceptTypes, $content);
  700. }
  701. $this->__typesInitialized = true;
  702. }
  703. }
  704. ?>