PageRenderTime 49ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/library/XenForo/Application.php

https://github.com/hanguyenhuu/DTUI_201105
PHP | 1025 lines | 607 code | 97 blank | 321 comment | 54 complexity | 623d8a694249e7ad98663a354b4c35f9 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, BSD-3-Clause
  1. <?php
  2. if (!defined('XENFORO_AUTOLOADER_SETUP')) { die('No access'); }
  3. /**
  4. * Base XenForo application class. Sets up the environment as necessary and acts as the
  5. * registry for the application. Can broker to the autoload as well.
  6. *
  7. * @package XenForo_Core
  8. */
  9. class XenForo_Application extends Zend_Registry
  10. {
  11. const URL_ID_DELIMITER = '.';
  12. /**
  13. * Current printable and encoded versions. These are used for visual output
  14. * and installation/upgrading.
  15. *
  16. * @var string
  17. * @var integer
  18. */
  19. public static $version = '1.0.1';
  20. public static $versionId = 1000170; // abbccde = a.b.c d (alpha: 1, beta: 3, RC: 5, stable: 7, PL: 9) e
  21. /**
  22. * JavaScript cache buster variable
  23. *
  24. * @var string
  25. */
  26. public static $jsVersion = '';
  27. /**
  28. * jQuery version currently in use. See XenForo_Dependencies_Public::getJquerySource()
  29. *
  30. * @var string
  31. */
  32. public static $jQueryVersion = '1.4.4';
  33. /**
  34. * Path to directory containing the application's configuration file(s).
  35. *
  36. * @var string
  37. */
  38. protected $_configDir = '.';
  39. /**
  40. * Path to applications root directory. Specific directories will be looked for within this.
  41. *
  42. * @var string
  43. */
  44. protected $_rootDir = '.';
  45. /**
  46. * Stores whether the application has been initialized yet.
  47. *
  48. * @var boolean
  49. */
  50. protected $_initialized = false;
  51. /**
  52. * Un-used lazy loaders for the registry. When a lazy loader is called, it
  53. * is removed from the list. Key is the index and value is an array:
  54. * 0 => callback
  55. * 1 => array of arguments
  56. *
  57. * @var array
  58. */
  59. protected $_lazyLoaders = array();
  60. /**
  61. * If true, any PHP errors/warnings/notices that come up will be handled
  62. * by our error handler. Otherwise, they will be deferred to any previously
  63. * registered handler (probably PHP's).
  64. *
  65. * @var boolean
  66. */
  67. protected static $_handlePhpError = true;
  68. /**
  69. * Controls whether the application is in debug mode.
  70. *
  71. * @var boolean
  72. */
  73. protected static $_debug;
  74. /**
  75. * Cache of random data. String of hex characters.
  76. *
  77. * @var string
  78. */
  79. protected static $_randomData = '';
  80. /**
  81. * Cache of dynamic inheritance classes and what they resolve to.
  82. *
  83. * @var array
  84. */
  85. protected static $_classCache = array();
  86. /**
  87. * Unix timestamp representing the current webserver date and time.
  88. * This should be used whenever 'now' needs to be referred to.
  89. *
  90. * @var integer
  91. */
  92. public static $time = 0;
  93. /**
  94. * Hostname of the server
  95. *
  96. * @var string
  97. */
  98. public static $host = 'localhost';
  99. /**
  100. * Are we using SSL?
  101. *
  102. * @var boolean
  103. */
  104. public static $secure = false;
  105. /**
  106. * Value we can use as a sentinel to stand for variable integer values
  107. *
  108. * @var string
  109. */
  110. public static $integerSentinel = '{{sentinel}}';
  111. /**
  112. * Relative path to the thumbnails / avatars (etc.) directory from the base installation directory.
  113. * Must be web accessible and server-writable.
  114. * Examples 'data', 'foo/bar/data', '../path/to/thingy'.
  115. *
  116. * @var string
  117. */
  118. public static $externalDataPath = 'data';
  119. /**
  120. * Begin the application. This causes the environment to be setup as necessary.
  121. *
  122. * @param string Path to application configuration directory. See {@link $_configDir}.
  123. * @param string Path to application root directory. See {@link $_rootDir}.
  124. * @param boolean True to load default data (config, DB, etc)
  125. */
  126. public function beginApplication($configDir = '.', $rootDir = '.', $loadDefaultData = true)
  127. {
  128. if ($this->_initialized)
  129. {
  130. return;
  131. }
  132. if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
  133. {
  134. self::undoMagicQuotes($_GET);
  135. self::undoMagicQuotes($_POST);
  136. self::undoMagicQuotes($_COOKIE);
  137. self::undoMagicQuotes($_REQUEST);
  138. }
  139. if (function_exists('get_magic_quotes_runtime') && get_magic_quotes_runtime())
  140. {
  141. @set_magic_quotes_runtime(false);
  142. }
  143. @ini_set('memory_limit', 128 * 1024 * 1024);
  144. ignore_user_abort(true);
  145. @ini_set('output_buffering', false);
  146. while (@ob_end_clean());
  147. error_reporting(E_ALL | E_STRICT & ~8192);
  148. set_error_handler(array('XenForo_Application', 'handlePhpError'));
  149. set_exception_handler(array('XenForo_Application', 'handleException'));
  150. //@ini_set('pcre.backtrack_limit', 1000000);
  151. date_default_timezone_set('UTC');
  152. self::$time = time();
  153. self::$host = (empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST']);
  154. self::$secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
  155. require(XenForo_Autoloader::getInstance()->autoloaderClassToFile('Lgpl_utf8'));
  156. $this->_configDir = $configDir;
  157. $this->_rootDir = $rootDir;
  158. $this->addLazyLoader('requestPaths', array($this, 'loadRequestPaths'));
  159. if ($loadDefaultData)
  160. {
  161. $this->loadDefaultData();
  162. }
  163. $this->_initialized = true;
  164. }
  165. /**
  166. * Loads the default data for the application (config, DB, options, etc).
  167. */
  168. public function loadDefaultData()
  169. {
  170. $config = $this->loadConfig();
  171. self::set('config', $config);
  172. self::setDebugMode($config->debug);
  173. self::$jsVersion = substr(md5(self::$versionId . $config->jsVersion), 0, 8);
  174. self::$externalDataPath = (string)$config->externalDataPath;
  175. $this->addLazyLoader('db', array($this, 'loadDb'), $config->db);
  176. $this->addLazyLoader('cache', array($this, 'loadCache'), $config->cache);
  177. $this->addLazyLoader('options', array($this, 'loadOptions'));
  178. $this->addLazyLoader('simpleCache', array($this, 'loadSimpleCache'));
  179. }
  180. /**
  181. * Helper function to initialize the application.
  182. *
  183. * @param string Path to application configuration directory. See {@link $_configDir}.
  184. * @param string Path to application root directory. See {@link $_rootDir}.
  185. * @param boolean True to load default data (config, DB, etc)
  186. */
  187. public static function initialize($configDir = '.', $rootDir = '.', $loadDefaultData = true)
  188. {
  189. self::setClassName(__CLASS__);
  190. self::getInstance()->beginApplication($configDir, $rootDir, $loadDefaultData);
  191. }
  192. /**
  193. * Handler for set_error_handler to convert notices, warnings, and other errors
  194. * into exceptions.
  195. *
  196. * @param integer $errorType Type of error (one of the E_* constants)
  197. * @param string $errorString
  198. * @param string $file
  199. * @param integer $line
  200. */
  201. public static function handlePhpError($errorType, $errorString, $file, $line)
  202. {
  203. if (!self::$_handlePhpError)
  204. {
  205. return false;
  206. }
  207. if ($errorType & error_reporting())
  208. {
  209. throw new ErrorException($errorString, 0, $errorType, $file, $line);
  210. }
  211. }
  212. /**
  213. * Disables our PHP error handler, in favor of a previously registered one
  214. * (or the default PHP error handler).
  215. */
  216. public static function disablePhpErrorHandler()
  217. {
  218. self::$_handlePhpError = false;
  219. }
  220. /**
  221. * Enables our PHP error handler.
  222. */
  223. public static function enablePhpErrorHandler()
  224. {
  225. self::$_handlePhpError = true;
  226. }
  227. /**
  228. * Default exception handler.
  229. *
  230. * @param Exception $e
  231. */
  232. public static function handleException(Exception $e)
  233. {
  234. XenForo_Error::logException($e);
  235. XenForo_Error::unexpectedException($e);
  236. }
  237. /**
  238. * Returns true if the application is in debug mode.
  239. *
  240. * @return boolean
  241. */
  242. public static function debugMode()
  243. {
  244. return self::$_debug;
  245. }
  246. /**
  247. * Sets the debug mode value.
  248. *
  249. * @param boolean $debug
  250. */
  251. public static function setDebugMode($debug)
  252. {
  253. self::$_debug = (boolean)$debug;
  254. if (self::$_debug)
  255. {
  256. @ini_set('display_errors', true);
  257. }
  258. }
  259. /**
  260. * Determines whether we should try to write to the development files.
  261. *
  262. * @return boolean
  263. */
  264. public static function canWriteDevelopmentFiles()
  265. {
  266. return (self::debugMode() && XenForo_Application::get('config')->development->directory);
  267. }
  268. /**
  269. * Resolves dynamic, run time inheritance for the specified class.
  270. * The classes to be loaded for this base class are grabbed via the event.
  271. * These classes must inherit from from XFCP_x, which is a non-existant
  272. * class that is dynamically created, inheriting from the correct class
  273. * as needed.
  274. *
  275. * If a fake base is needed when the base class doesn't exist, and there
  276. * are no classes extending it, false will still be returned! This prevents
  277. * an unnecessary eval.
  278. *
  279. * @param string $class Name of class
  280. * @param string $type Type of class (for determining event to fire)
  281. * @param string|false $fakeBase If the specified class doesn't exist, an alternative base can be specified
  282. *
  283. * @return false|string False or name of class to instantiate
  284. */
  285. public static function resolveDynamicClass($class, $type, $fakeBase = false)
  286. {
  287. if (!XenForo_Application::autoload($class))
  288. {
  289. if ($fakeBase)
  290. {
  291. $fakeNeeded = true;
  292. }
  293. else
  294. {
  295. return false;
  296. }
  297. }
  298. else
  299. {
  300. $fakeNeeded = false;
  301. }
  302. if (!empty(self::$_classCache[$class]))
  303. {
  304. return self::$_classCache[$class];
  305. }
  306. $createClass = $class;
  307. $extend = array();
  308. XenForo_CodeEvent::fire('load_class_' . $type, array($class, &$extend));
  309. if ($fakeNeeded)
  310. {
  311. if (!$extend)
  312. {
  313. return false;
  314. }
  315. eval('class ' . $class . ' extends ' . $fakeBase . ' {}');
  316. }
  317. if ($extend)
  318. {
  319. try
  320. {
  321. foreach ($extend AS $dynamicClass)
  322. {
  323. // XenForo Class Proxy, in case you're wondering
  324. $proxyClass = 'XFCP_' . $dynamicClass;
  325. eval('class ' . $proxyClass . ' extends ' . $createClass . ' {}');
  326. XenForo_Application::autoload($dynamicClass);
  327. $createClass = $dynamicClass;
  328. }
  329. }
  330. catch (Exception $e)
  331. {
  332. self::$_classCache[$class] = $class;
  333. throw $e;
  334. }
  335. }
  336. self::$_classCache[$class] = $createClass;
  337. return $createClass;
  338. }
  339. /**
  340. * Gets the path to the configuration directory.
  341. *
  342. * @return string
  343. */
  344. public function getConfigDir()
  345. {
  346. return $this->_configDir;
  347. }
  348. /**
  349. * Gets the path to the application root directory.
  350. *
  351. * @return string
  352. */
  353. public function getRootDir()
  354. {
  355. return $this->_rootDir;
  356. }
  357. /**
  358. * Load the configuration file. Mixes in over top of the default values. Provided
  359. * a default is specified in {@link loadDefaultConfig}, all elements available
  360. * to the config will always be defined. Non-default elements may still be defined
  361. * in the loaded configuration.
  362. *
  363. * @return Zend_Config
  364. */
  365. public function loadConfig()
  366. {
  367. if (file_exists($this->_configDir . '/config.php'))
  368. {
  369. $defaultConfig = $this->loadDefaultConfig();
  370. $config = array();
  371. require($this->_configDir . '/config.php');
  372. $outputConfig = new Zend_Config(array(), true);
  373. $outputConfig->merge($defaultConfig)
  374. ->merge(new Zend_Config($config))
  375. ->setReadOnly();
  376. return $outputConfig;
  377. }
  378. else
  379. {
  380. if (XenForo_Model::create('XenForo_Install_Model_Install')->isInstalled())
  381. {
  382. // TODO: ideally, we want a better way to display a fatal error like this
  383. echo "Couldn't load library/config.php file.";
  384. exit;
  385. }
  386. else
  387. {
  388. header('Location: install/index.php');
  389. exit;
  390. }
  391. }
  392. }
  393. /**
  394. * Load the default configuration. User-specified versions will override this.
  395. *
  396. * @return Zend_Config
  397. */
  398. public function loadDefaultConfig()
  399. {
  400. return new Zend_Config(array(
  401. 'db' => array(
  402. 'adapter' => 'mysqli',
  403. 'host' => 'localhost',
  404. 'port' => '3306',
  405. 'username' => '',
  406. 'password' => '',
  407. 'dbname' => ''
  408. ),
  409. 'cache' => array(
  410. 'enabled' => false,
  411. 'frontend' => 'core',
  412. 'frontendOptions' => array(
  413. 'caching' => true,
  414. 'cache_id_prefix' => 'xf_'
  415. ),
  416. 'backend' => 'file',
  417. 'backendOptions' => array(
  418. 'file_name_prefix' => 'xf_'
  419. )
  420. ),
  421. 'debug' => false,
  422. 'enableListeners' => true,
  423. 'development' => array(
  424. 'directory' => '', // relative to the configuration directory
  425. 'default_addon' => ''
  426. ),
  427. 'superAdmins' => '1',
  428. 'globalSalt' => '4984f8a5687709e87712c8b82164faf9',
  429. 'jsVersion' => '',
  430. 'cookie' => array(
  431. 'prefix' => 'xf_',
  432. 'path' => '/',
  433. 'domain' => ''
  434. ),
  435. 'enableMail' => true,
  436. 'internalDataPath' => 'internal_data',
  437. 'externalDataPath' => 'data',
  438. 'checkVersion' => true,
  439. 'enableGzip' => true,
  440. 'enableContentLength' => true,
  441. ));
  442. }
  443. /**
  444. * Load the database object.
  445. *
  446. * @param Zend_Configuration Configuration to use
  447. *
  448. * @return Zend_Db_Adapter_Abstract
  449. */
  450. public function loadDb(Zend_Config $dbConfig)
  451. {
  452. $db = Zend_Db::factory($dbConfig->adapter,
  453. array(
  454. 'host' => $dbConfig->host,
  455. 'port' => $dbConfig->port,
  456. 'username' => $dbConfig->username,
  457. 'password' => $dbConfig->password,
  458. 'dbname' => $dbConfig->dbname,
  459. 'charset' => 'utf8'
  460. )
  461. );
  462. switch (get_class($db))
  463. {
  464. case 'Zend_Db_Adapter_Mysqli':
  465. $db->getConnection()->query("SET @@session.sql_mode='STRICT_ALL_TABLES'");
  466. break;
  467. case 'Zend_Db_Adapter_Pdo_Mysql':
  468. $db->getConnection()->exec("SET @@session.sql_mode='STRICT_ALL_TABLES'");
  469. break;
  470. }
  471. if (self::debugMode())
  472. {
  473. $db->setProfiler(true);
  474. }
  475. return $db;
  476. }
  477. /**
  478. * Load the cache object.
  479. *
  480. * @param Zend_Configuration Configuration to use
  481. *
  482. * @return Zend_Cache_Core|Zend_Cache_Frontend|false
  483. */
  484. public function loadCache(Zend_Config $cacheConfig)
  485. {
  486. if (!$cacheConfig->enabled)
  487. {
  488. return false;
  489. }
  490. return Zend_Cache::factory(
  491. $cacheConfig->frontend,
  492. $cacheConfig->backend,
  493. $cacheConfig->frontendOptions->toArray(),
  494. $cacheConfig->backendOptions->toArray()
  495. );
  496. }
  497. /**
  498. * Loads the list of options from the cache if possible and rebuilds
  499. * it from the DB if necessary.
  500. *
  501. * @return XenForo_Options
  502. */
  503. public function loadOptions()
  504. {
  505. $options = XenForo_Model::create('XenForo_Model_DataRegistry')->get('options');
  506. if (!is_array($options))
  507. {
  508. $options = XenForo_Model::create('XenForo_Model_Option')->rebuildOptionCache();
  509. }
  510. $optionsObj = new XenForo_Options($options);
  511. self::setDefaultsFromOptions($optionsObj);
  512. return $optionsObj;
  513. }
  514. /**
  515. * Setup necessary system defaults based on the options.
  516. *
  517. * @param XenForo_Options $options
  518. */
  519. public static function setDefaultsFromOptions(XenForo_Options $options)
  520. {
  521. if ($options->useFriendlyUrls)
  522. {
  523. XenForo_Link::useFriendlyUrls(true);
  524. }
  525. }
  526. /**
  527. * Loads the request paths from a default request object.
  528. *
  529. * @return array
  530. */
  531. public function loadRequestPaths()
  532. {
  533. return self::getRequestPaths(new Zend_Controller_Request_Http());
  534. }
  535. /**
  536. * Gets the request paths from the specified request object.
  537. *
  538. * @param Zend_Controller_Request_Http $request
  539. *
  540. * @return array Keys: basePath, host, protocol, fullBasePath, requestUri
  541. */
  542. public static function getRequestPaths(Zend_Controller_Request_Http $request)
  543. {
  544. $basePath = $request->getBasePath();
  545. if ($basePath === '' || substr($basePath, -1) != '/')
  546. {
  547. $basePath .= '/';
  548. }
  549. $host = $request->getServer('HTTP_HOST');
  550. if (!$host)
  551. {
  552. $host = $request->getServer('SERVER_NAME');
  553. $serverPort = intval($request->getServer('SERVER_PORT'));
  554. if ($serverPort && $serverPort != 80 && $serverPort != 443)
  555. {
  556. $host .= ':' . $serverPort;
  557. }
  558. }
  559. $protocol = ($request->isSecure() ? 'https' : 'http');
  560. $requestUri = $request->getRequestUri();
  561. return array(
  562. 'basePath' => $basePath,
  563. 'host' => $host,
  564. 'protocol' => $protocol,
  565. 'fullBasePath' => $protocol . '://' . $host . $basePath,
  566. 'requestUri' => $requestUri,
  567. 'fullUri' => $protocol . '://' . $host . $requestUri
  568. );
  569. }
  570. /**
  571. * Add a lazy loader to the application registry. This lazy loader callback
  572. * will be called if the specified index is not in the registry.
  573. *
  574. * The 3rd argument and on will be passed to the lazy loader callback.
  575. *
  576. * @param string Index to assign lazy loader to
  577. * @param callback Callback to call when triggered
  578. */
  579. public function addLazyLoader($index, $callback)
  580. {
  581. if (!is_callable($callback, true))
  582. {
  583. throw new Zend_Exception("Invalid callback for lazy loading '$index'");
  584. }
  585. $arguments = array_slice(func_get_args(), 2);
  586. $this->_lazyLoaders[$index] = array($callback, $arguments);
  587. }
  588. /**
  589. * Removes the lazy loader from the specified index.
  590. *
  591. * @param string Index to remove from
  592. *
  593. * @return boolean
  594. */
  595. public function removeLazyLoader($index)
  596. {
  597. if (isset($this->_lazyLoaders[$index]))
  598. {
  599. unset($this->_lazyLoaders[$index]);
  600. return true;
  601. }
  602. else
  603. {
  604. return false;
  605. }
  606. }
  607. /**
  608. * Loads simple cache data from the source.
  609. *
  610. * @return array
  611. */
  612. public function loadSimpleCache()
  613. {
  614. $return = XenForo_Model::create('XenForo_Model_DataRegistry')->get('simpleCache');
  615. return (is_array($return) ? $return : array());
  616. }
  617. /**
  618. * Gets the specified simple cache data. The simple cache is for data that you want
  619. * available on on pages, but don't need to special rebuild behaviors for.
  620. *
  621. * @param string $key
  622. *
  623. * @return mixed|false False if not in the cache
  624. */
  625. public static function getSimpleCacheData($key)
  626. {
  627. $cache = self::get('simpleCache');
  628. return (isset($cache[$key]) ? $cache[$key] : false);
  629. }
  630. /**
  631. * Sets the specified simple cache data. This data will be persisted over pages
  632. * indefinitely. Values of false will remove the cache data.
  633. *
  634. * @param string $key
  635. * @param mixed $value If false, the specified cache key is removed
  636. */
  637. public static function setSimpleCacheData($key, $value)
  638. {
  639. $cache = self::get('simpleCache');
  640. if ($value === false)
  641. {
  642. unset($cache[$key]);
  643. }
  644. else
  645. {
  646. $cache[$key] = $value;
  647. }
  648. XenForo_Model::create('XenForo_Model_DataRegistry')->set('simpleCache', $cache);
  649. self::set('simpleCache', $cache);
  650. }
  651. /**
  652. * Execute lazy loader for an index if there is one. The loaded data is returned
  653. * via a reference parameter, not the return value of the method. The return
  654. * value is true only if the lazy loader was executed.
  655. *
  656. * Once called, the data is set to the registry and the lazy loader is removed.
  657. *
  658. * @param string Index to lazy load
  659. * @param mixed By ref; data returned by lazy loader
  660. *
  661. * @return boolean True if a lazy loader was called
  662. */
  663. public function lazyLoad($index, &$return)
  664. {
  665. if (isset($this->_lazyLoaders[$index]))
  666. {
  667. $lazyLoader = $this->_lazyLoaders[$index];
  668. $return = call_user_func_array($lazyLoader[0], $lazyLoader[1]);
  669. $this->offsetSet($index, $return);
  670. $this->removeLazyLoader($index);
  671. return true;
  672. }
  673. else
  674. {
  675. return false;
  676. }
  677. }
  678. /**
  679. * getter method, basically same as offsetGet().
  680. *
  681. * This method can be called from an object of type Zend_Registry, or it
  682. * can be called statically. In the latter case, it uses the default
  683. * static instance stored in the class.
  684. *
  685. * @param string $index - get the value associated with $index
  686. * @return mixed
  687. * @throws Zend_Exception if no entry is registerd for $index.
  688. */
  689. public static function get($index)
  690. {
  691. $instance = self::getInstance();
  692. if (!$instance->offsetExists($index))
  693. {
  694. if ($instance->lazyLoad($index, $return))
  695. {
  696. return $return;
  697. }
  698. else
  699. {
  700. throw new Zend_Exception("No entry is registered for key '$index'");
  701. }
  702. }
  703. return $instance->offsetGet($index);
  704. }
  705. /**
  706. * Attempts to get the specified index. If it cannot be found, the callback
  707. * is called and the result from the callback is set into the registry for that
  708. * index.
  709. *
  710. * @param string $index Index to look for
  711. * @param callback $callback Callback function to call if not found
  712. * @param array $args Arguments to pass to callback
  713. *
  714. * @return mixed
  715. */
  716. public static function getWithFallback($index, $callback, array $args = array())
  717. {
  718. if (self::isRegistered($index))
  719. {
  720. return self::get($index);
  721. }
  722. else
  723. {
  724. $result = call_user_func_array($callback, $args);
  725. self::set($index, $result);
  726. return $result;
  727. }
  728. }
  729. /**
  730. * Helper method to autoload a class. Could simply call the autoloader directly
  731. * but this method is recommended to reduce dependencies.
  732. *
  733. * @param string $class Class to load
  734. *
  735. * @return boolean
  736. */
  737. public static function autoload($class)
  738. {
  739. return XenForo_Autoloader::getInstance()->autoload($class);
  740. }
  741. /**
  742. * Helper method to remove the result of magic_quotes_gpc being applied to the
  743. * input super globals
  744. *
  745. * @param array The array to have slashes stripped, this is passed by reference
  746. * @param integer Recursion depth to prevent malicious use
  747. */
  748. public static function undoMagicQuotes(&$array, $depth = 0)
  749. {
  750. if ($depth > 10 || !is_array($array))
  751. {
  752. return;
  753. }
  754. foreach ($array AS $key => $value)
  755. {
  756. if (is_array($value))
  757. {
  758. self::undoMagicQuotes($array[$key], $depth + 1);
  759. }
  760. else
  761. {
  762. $array[$key] = stripslashes($value);
  763. }
  764. if (is_string($key))
  765. {
  766. $new_key = stripslashes($key);
  767. if ($new_key != $key)
  768. {
  769. $array[$new_key] = $array[$key];
  770. unset($array[$key]);
  771. }
  772. }
  773. }
  774. }
  775. /**
  776. * Gzips the given content if the browser supports it.
  777. *
  778. * @param string $content Content to gzip; this will be modified if necessary
  779. *
  780. * @return array List of HTTP headers to add
  781. */
  782. public static function gzipContentIfSupported(&$content)
  783. {
  784. if (@ini_get('zlib.output_compression'))
  785. {
  786. return array();
  787. }
  788. if (!function_exists('gzencode') || empty($_SERVER['HTTP_ACCEPT_ENCODING']))
  789. {
  790. return array();
  791. }
  792. if (!is_string($content))
  793. {
  794. return array();
  795. }
  796. if (!self::get('config')->enableGzip)
  797. {
  798. return array();
  799. }
  800. $headers = array();
  801. if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false)
  802. {
  803. $headers[] = array('Content-Encoding', 'gzip', true);
  804. $headers[] = array('Vary', 'Accept-Encoding', false);
  805. $content = gzencode($content, 1);
  806. }
  807. return $headers;
  808. }
  809. /**
  810. * Returns a version of the input $data that contains only the array keys defined in $keys
  811. *
  812. * Example: arrayFilterKeys(array('a' => 1, 'b' => 2, 'c' => 3), array('b', 'c'))
  813. * Returns: array('b' => 2, 'c' => 3)
  814. *
  815. * @param array $data
  816. * @param array $keys
  817. *
  818. * @return array $data
  819. */
  820. public static function arrayFilterKeys(array $data, array $keys)
  821. {
  822. // this version will not warn on undefined indexes: return array_intersect_key($data, array_flip($keys));
  823. $array = array();
  824. foreach ($keys AS $key)
  825. {
  826. $array[$key] = $data[$key];
  827. }
  828. return $array;
  829. }
  830. /**
  831. * This is a simplified version of a function similar to array_merge_recursive. It is
  832. * designed to recursively merge associative arrays (maps). If each array shares a key,
  833. * that key is recursed and the child keys are merged.
  834. *
  835. * This function does not handle merging of non-associative arrays (numeric keys) as
  836. * a special case.
  837. *
  838. * More than 2 arguments may be passed if desired.
  839. *
  840. * @param array $first
  841. * @param array $second
  842. *
  843. * @return array
  844. */
  845. public static function mapMerge(array $first, array $second)
  846. {
  847. $s = microtime(true);
  848. $args = func_get_args();
  849. unset($args[0]);
  850. foreach ($args AS $arg)
  851. {
  852. if (!is_array($arg) || !$arg)
  853. {
  854. continue;
  855. }
  856. foreach ($arg AS $key => $value)
  857. {
  858. if (array_key_exists($key, $first) && is_array($value) && is_array($first[$key]))
  859. {
  860. $first[$key] = self::mapMerge($first[$key], $value);
  861. }
  862. else
  863. {
  864. $first[$key] = $value;
  865. }
  866. }
  867. }
  868. return $first;
  869. }
  870. /**
  871. * Parses a query string (x=y&a=b&c[]=d) into a structured array format.
  872. *
  873. * @param string $string
  874. *
  875. * @return array
  876. */
  877. public static function parseQueryString($string)
  878. {
  879. parse_str($string, $output);
  880. if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
  881. {
  882. XenForo_Application::undoMagicQuotes($output);
  883. }
  884. return $output;
  885. }
  886. /**
  887. * Generates a psuedo-random string of the specified length.
  888. *
  889. * @param integer $length
  890. *
  891. * @return string
  892. */
  893. public static function generateRandomString($length)
  894. {
  895. while (strlen(self::$_randomData) < $length)
  896. {
  897. // openssl_random_pseudo_bytes is *ridiculously* slow on windows
  898. if (function_exists('openssl_random_pseudo_bytes') && substr(PHP_OS, 0, 3) != 'WIN')
  899. {
  900. self::$_randomData .= bin2hex(openssl_random_pseudo_bytes(max($length, 1024) / 2));
  901. }
  902. else
  903. {
  904. self::$_randomData .= md5(uniqid(mt_rand(), true));
  905. }
  906. }
  907. $return = substr(self::$_randomData, 0, $length);
  908. self::$_randomData = substr(self::$_randomData, $length);
  909. return $return;
  910. }
  911. }