sfToolkit.class.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
  5. * (c) 2004-2006 Sean Kerr <sean@code-box.org>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. /**
  11. * sfToolkit provides basic utility methods.
  12. *
  13. * @package symfony
  14. * @subpackage util
  15. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  16. * @author Sean Kerr <sean@code-box.org>
  17. * @version SVN: $Id: sfToolkit.class.php 19980 2009-07-07 16:29:54Z nicolas $
  18. */
  19. class sfToolkit
  20. {
  21. /**
  22. * Extract the class or interface name from filename.
  23. *
  24. * @param string $filename A filename.
  25. *
  26. * @return string A class or interface name, if one can be extracted, otherwise null.
  27. */
  28. public static function extractClassName($filename)
  29. {
  30. $retval = null;
  31. if (self::isPathAbsolute($filename))
  32. {
  33. $filename = basename($filename);
  34. }
  35. $pattern = '/(.*?)\.(class|interface)\.php/i';
  36. if (preg_match($pattern, $filename, $match))
  37. {
  38. $retval = $match[1];
  39. }
  40. return $retval;
  41. }
  42. /**
  43. * Clear all files in a given directory.
  44. *
  45. * @param string $directory An absolute filesystem path to a directory.
  46. */
  47. public static function clearDirectory($directory)
  48. {
  49. if (!is_dir($directory))
  50. {
  51. return;
  52. }
  53. // open a file point to the cache dir
  54. $fp = opendir($directory);
  55. // ignore names
  56. $ignore = array('.', '..', 'CVS', '.svn');
  57. while (($file = readdir($fp)) !== false)
  58. {
  59. if (!in_array($file, $ignore))
  60. {
  61. if (is_link($directory.'/'.$file))
  62. {
  63. // delete symlink
  64. unlink($directory.'/'.$file);
  65. }
  66. else if (is_dir($directory.'/'.$file))
  67. {
  68. // recurse through directory
  69. self::clearDirectory($directory.'/'.$file);
  70. // delete the directory
  71. rmdir($directory.'/'.$file);
  72. }
  73. else
  74. {
  75. // delete the file
  76. unlink($directory.'/'.$file);
  77. }
  78. }
  79. }
  80. // close file pointer
  81. closedir($fp);
  82. }
  83. /**
  84. * Clear all files and directories corresponding to a glob pattern.
  85. *
  86. * @param string $pattern An absolute filesystem pattern.
  87. */
  88. public static function clearGlob($pattern)
  89. {
  90. $files = glob($pattern);
  91. // order is important when removing directories
  92. sort($files);
  93. foreach ($files as $file)
  94. {
  95. if (is_dir($file))
  96. {
  97. // delete directory
  98. self::clearDirectory($file);
  99. }
  100. else
  101. {
  102. // delete file
  103. unlink($file);
  104. }
  105. }
  106. }
  107. /**
  108. * Determine if a filesystem path is absolute.
  109. *
  110. * @param path $path A filesystem path.
  111. *
  112. * @return bool true, if the path is absolute, otherwise false.
  113. */
  114. public static function isPathAbsolute($path)
  115. {
  116. if ($path[0] == '/' || $path[0] == '\\' ||
  117. (strlen($path) > 3 && ctype_alpha($path[0]) &&
  118. $path[1] == ':' &&
  119. ($path[2] == '\\' || $path[2] == '/')
  120. )
  121. )
  122. {
  123. return true;
  124. }
  125. return false;
  126. }
  127. /**
  128. * Determine if a lock file is present.
  129. *
  130. * @param string $lockFile Name of the lock file.
  131. * @param integer $maxLockFileLifeTime A max amount of life time for the lock file.
  132. *
  133. * @return bool true, if the lock file is present, otherwise false.
  134. */
  135. public static function hasLockFile($lockFile, $maxLockFileLifeTime = 0)
  136. {
  137. $isLocked = false;
  138. if (is_readable($lockFile) && ($last_access = fileatime($lockFile)))
  139. {
  140. $now = time();
  141. $timeDiff = $now - $last_access;
  142. if (!$maxLockFileLifeTime || $timeDiff < $maxLockFileLifeTime)
  143. {
  144. $isLocked = true;
  145. }
  146. else
  147. {
  148. $isLocked = @unlink($lockFile) ? false : true;
  149. }
  150. }
  151. return $isLocked;
  152. }
  153. /**
  154. * Strips comments from php source code
  155. *
  156. * @param string $source PHP source code.
  157. *
  158. * @return string Comment free source code.
  159. */
  160. public static function stripComments($source)
  161. {
  162. if (!sfConfig::get('sf_strip_comments', true) || !function_exists('token_get_all'))
  163. {
  164. return $source;
  165. }
  166. $ignore = array(T_COMMENT => true, T_DOC_COMMENT => true);
  167. $output = '';
  168. foreach (token_get_all($source) as $token)
  169. {
  170. // array
  171. if (isset($token[1]))
  172. {
  173. // no action on comments
  174. if (!isset($ignore[$token[0]]))
  175. {
  176. // anything else -> output "as is"
  177. $output .= $token[1];
  178. }
  179. }
  180. else
  181. {
  182. // simple 1-character token
  183. $output .= $token;
  184. }
  185. }
  186. return $output;
  187. }
  188. /**
  189. * Strip slashes recursively from array
  190. *
  191. * @param array $value the value to strip
  192. *
  193. * @return array clean value with slashes stripped
  194. */
  195. public static function stripslashesDeep($value)
  196. {
  197. return is_array($value) ? array_map(array('sfToolkit', 'stripslashesDeep'), $value) : stripslashes($value);
  198. }
  199. // code from php at moechofe dot com (array_merge comment on php.net)
  200. /*
  201. * array arrayDeepMerge ( array array1 [, array array2 [, array ...]] )
  202. *
  203. * Like array_merge
  204. *
  205. * arrayDeepMerge() merges the elements of one or more arrays together so
  206. * that the values of one are appended to the end of the previous one. It
  207. * returns the resulting array.
  208. * If the input arrays have the same string keys, then the later value for
  209. * that key will overwrite the previous one. If, however, the arrays contain
  210. * numeric keys, the later value will not overwrite the original value, but
  211. * will be appended.
  212. * If only one array is given and the array is numerically indexed, the keys
  213. * get reindexed in a continuous way.
  214. *
  215. * Different from array_merge
  216. * If string keys have arrays for values, these arrays will merge recursively.
  217. */
  218. public static function arrayDeepMerge()
  219. {
  220. switch (func_num_args())
  221. {
  222. case 0:
  223. return false;
  224. case 1:
  225. return func_get_arg(0);
  226. case 2:
  227. $args = func_get_args();
  228. $args[2] = array();
  229. if (is_array($args[0]) && is_array($args[1]))
  230. {
  231. foreach (array_unique(array_merge(array_keys($args[0]),array_keys($args[1]))) as $key)
  232. {
  233. $isKey0 = array_key_exists($key, $args[0]);
  234. $isKey1 = array_key_exists($key, $args[1]);
  235. if ($isKey0 && $isKey1 && is_array($args[0][$key]) && is_array($args[1][$key]))
  236. {
  237. $args[2][$key] = self::arrayDeepMerge($args[0][$key], $args[1][$key]);
  238. }
  239. else if ($isKey0 && $isKey1)
  240. {
  241. $args[2][$key] = $args[1][$key];
  242. }
  243. else if (!$isKey1)
  244. {
  245. $args[2][$key] = $args[0][$key];
  246. }
  247. else if (!$isKey0)
  248. {
  249. $args[2][$key] = $args[1][$key];
  250. }
  251. }
  252. return $args[2];
  253. }
  254. else
  255. {
  256. return $args[1];
  257. }
  258. default :
  259. $args = func_get_args();
  260. $args[1] = sfToolkit::arrayDeepMerge($args[0], $args[1]);
  261. array_shift($args);
  262. return call_user_func_array(array('sfToolkit', 'arrayDeepMerge'), $args);
  263. break;
  264. }
  265. }
  266. /**
  267. * Converts string to array
  268. *
  269. * @param string $string the value to convert to array
  270. *
  271. * @return array
  272. */
  273. public static function stringToArray($string)
  274. {
  275. preg_match_all('/
  276. \s*(\w+) # key \\1
  277. \s*=\s* # =
  278. (\'|")? # values may be included in \' or " \\2
  279. (.*?) # value \\3
  280. (?(2) \\2) # matching \' or " if needed \\4
  281. \s*(?:
  282. (?=\w+\s*=) | \s*$ # followed by another key= or the end of the string
  283. )
  284. /x', $string, $matches, PREG_SET_ORDER);
  285. $attributes = array();
  286. foreach ($matches as $val)
  287. {
  288. $attributes[$val[1]] = self::literalize($val[3]);
  289. }
  290. return $attributes;
  291. }
  292. /**
  293. * Finds the type of the passed value, returns the value as the new type.
  294. *
  295. * @param string $value
  296. * @param bool $quoted Quote?
  297. *
  298. * @return mixed
  299. */
  300. public static function literalize($value, $quoted = false)
  301. {
  302. // lowercase our value for comparison
  303. $value = trim($value);
  304. $lvalue = strtolower($value);
  305. if (in_array($lvalue, array('null', '~', '')))
  306. {
  307. $value = null;
  308. }
  309. else if (in_array($lvalue, array('true', 'on', '+', 'yes')))
  310. {
  311. $value = true;
  312. }
  313. else if (in_array($lvalue, array('false', 'off', '-', 'no')))
  314. {
  315. $value = false;
  316. }
  317. else if (ctype_digit($value))
  318. {
  319. $value = (int) $value;
  320. }
  321. else if (is_numeric($value))
  322. {
  323. $value = (float) $value;
  324. }
  325. else
  326. {
  327. $value = self::replaceConstants($value);
  328. if ($quoted)
  329. {
  330. $value = '\''.str_replace('\'', '\\\'', $value).'\'';
  331. }
  332. }
  333. return $value;
  334. }
  335. /**
  336. * Replaces constant identifiers in a scalar value.
  337. *
  338. * @param string $value the value to perform the replacement on
  339. *
  340. * @return string the value with substitutions made
  341. */
  342. public static function replaceConstants($value)
  343. {
  344. return is_string($value) ? preg_replace_callback('/%(.+?)%/', create_function('$v', 'return sfConfig::has(strtolower($v[1])) ? sfConfig::get(strtolower($v[1])) : "%{$v[1]}%";'), $value) : $value;
  345. }
  346. /**
  347. * Returns subject replaced with regular expression matchs
  348. *
  349. * @param mixed $search subject to search
  350. * @param array $replacePairs array of search => replace pairs
  351. */
  352. public static function pregtr($search, $replacePairs)
  353. {
  354. return preg_replace(array_keys($replacePairs), array_values($replacePairs), $search);
  355. }
  356. /**
  357. * Checks if array values are empty
  358. *
  359. * @param array $array the array to check
  360. * @return boolean true if empty, otherwise false
  361. */
  362. public static function isArrayValuesEmpty($array)
  363. {
  364. static $isEmpty = true;
  365. foreach ($array as $value)
  366. {
  367. $isEmpty = (is_array($value)) ? self::isArrayValuesEmpty($value) : (strlen($value) == 0);
  368. if (!$isEmpty)
  369. {
  370. break;
  371. }
  372. }
  373. return $isEmpty;
  374. }
  375. /**
  376. * Checks if a string is an utf8.
  377. *
  378. * Yi Stone Li<yili@yahoo-inc.com>
  379. * Copyright (c) 2007 Yahoo! Inc. All rights reserved.
  380. * Licensed under the BSD open source license
  381. *
  382. * @param string
  383. *
  384. * @return bool true if $string is valid UTF-8 and false otherwise.
  385. */
  386. public static function isUTF8($string)
  387. {
  388. for ($idx = 0, $strlen = strlen($string); $idx < $strlen; $idx++)
  389. {
  390. $byte = ord($string[$idx]);
  391. if ($byte & 0x80)
  392. {
  393. if (($byte & 0xE0) == 0xC0)
  394. {
  395. // 2 byte char
  396. $bytes_remaining = 1;
  397. }
  398. else if (($byte & 0xF0) == 0xE0)
  399. {
  400. // 3 byte char
  401. $bytes_remaining = 2;
  402. }
  403. else if (($byte & 0xF8) == 0xF0)
  404. {
  405. // 4 byte char
  406. $bytes_remaining = 3;
  407. }
  408. else
  409. {
  410. return false;
  411. }
  412. if ($idx + $bytes_remaining >= $strlen)
  413. {
  414. return false;
  415. }
  416. while ($bytes_remaining--)
  417. {
  418. if ((ord($string[++$idx]) & 0xC0) != 0x80)
  419. {
  420. return false;
  421. }
  422. }
  423. }
  424. }
  425. return true;
  426. }
  427. /**
  428. * Returns a reference to an array value for a path.
  429. *
  430. * @param array $values The values to search
  431. * @param string $name The token name
  432. * @param array $default Default if not found
  433. *
  434. * @return array reference
  435. */
  436. public static function &getArrayValueForPathByRef(&$values, $name, $default = null)
  437. {
  438. if (false === $offset = strpos($name, '['))
  439. {
  440. $return = $default;
  441. if (isset($values[$name]))
  442. {
  443. $return = &$values[$name];
  444. }
  445. return $return;
  446. }
  447. if (!isset($values[substr($name, 0, $offset)]))
  448. {
  449. return $default;
  450. }
  451. $array = &$values[substr($name, 0, $offset)];
  452. while (false !== $pos = strpos($name, '[', $offset))
  453. {
  454. $end = strpos($name, ']', $pos);
  455. if ($end == $pos + 1)
  456. {
  457. // reached a []
  458. if (!is_array($array))
  459. {
  460. return $default;
  461. }
  462. break;
  463. }
  464. else if (!isset($array[substr($name, $pos + 1, $end - $pos - 1)]))
  465. {
  466. return $default;
  467. }
  468. else if (is_array($array))
  469. {
  470. $array = &$array[substr($name, $pos + 1, $end - $pos - 1)];
  471. $offset = $end;
  472. }
  473. else
  474. {
  475. return $default;
  476. }
  477. }
  478. return $array;
  479. }
  480. /**
  481. * Returns an array value for a path.
  482. *
  483. * @param array $values The values to search
  484. * @param string $name The token name
  485. * @param array $default Default if not found
  486. *
  487. * @return array
  488. */
  489. public static function getArrayValueForPath($values, $name, $default = null)
  490. {
  491. if (false === $offset = strpos($name, '['))
  492. {
  493. return isset($values[$name]) ? $values[$name] : $default;
  494. }
  495. if (!isset($values[substr($name, 0, $offset)]))
  496. {
  497. return $default;
  498. }
  499. $array = $values[substr($name, 0, $offset)];
  500. while (false !== $pos = strpos($name, '[', $offset))
  501. {
  502. $end = strpos($name, ']', $pos);
  503. if ($end == $pos + 1)
  504. {
  505. // reached a []
  506. if (!is_array($array))
  507. {
  508. return $default;
  509. }
  510. break;
  511. }
  512. else if (!isset($array[substr($name, $pos + 1, $end - $pos - 1)]))
  513. {
  514. return $default;
  515. }
  516. else if (is_array($array))
  517. {
  518. $array = $array[substr($name, $pos + 1, $end - $pos - 1)];
  519. $offset = $end;
  520. }
  521. else
  522. {
  523. return $default;
  524. }
  525. }
  526. return $array;
  527. }
  528. /**
  529. * Returns true if the a path exists for the given array.
  530. *
  531. * @param array $values The values to search
  532. * @param string $name The token name
  533. *
  534. * @return bool
  535. */
  536. public static function hasArrayValueForPath($values, $name)
  537. {
  538. if (false === $offset = strpos($name, '['))
  539. {
  540. return array_key_exists($name, $values);
  541. }
  542. if (!isset($values[substr($name, 0, $offset)]))
  543. {
  544. return false;
  545. }
  546. $array = $values[substr($name, 0, $offset)];
  547. while (false !== $pos = strpos($name, '[', $offset))
  548. {
  549. $end = strpos($name, ']', $pos);
  550. if ($end == $pos + 1)
  551. {
  552. // reached a []
  553. return is_array($array);
  554. }
  555. else if (!isset($array[substr($name, $pos + 1, $end - $pos - 1)]))
  556. {
  557. return false;
  558. }
  559. else if (is_array($array))
  560. {
  561. $array = $array[substr($name, $pos + 1, $end - $pos - 1)];
  562. $offset = $end;
  563. }
  564. else
  565. {
  566. return false;
  567. }
  568. }
  569. return true;
  570. }
  571. /**
  572. * Removes a path for the given array.
  573. *
  574. * @param array $values The values to search
  575. * @param string $name The token name
  576. * @param mixed $default The default value to return if the name does not exist
  577. */
  578. public static function removeArrayValueForPath(&$values, $name, $default = null)
  579. {
  580. if (false === $offset = strpos($name, '['))
  581. {
  582. if (isset($values[$name]))
  583. {
  584. $value = $values[$name];
  585. unset($values[$name]);
  586. return $value;
  587. }
  588. else
  589. {
  590. return $default;
  591. }
  592. }
  593. if (!isset($values[substr($name, 0, $offset)]))
  594. {
  595. return $default;
  596. }
  597. $value = &$values[substr($name, 0, $offset)];
  598. while (false !== $pos = strpos($name, '[', $offset))
  599. {
  600. $end = strpos($name, ']', $pos);
  601. if ($end == $pos + 1)
  602. {
  603. // reached a []
  604. if (!is_array($value))
  605. {
  606. return $default;
  607. }
  608. break;
  609. }
  610. else if (!isset($value[substr($name, $pos + 1, $end - $pos - 1)]))
  611. {
  612. return $default;
  613. }
  614. else if (is_array($value))
  615. {
  616. $parent = &$value;
  617. $key = substr($name, $pos + 1, $end - $pos - 1);
  618. $value = &$value[$key];
  619. $offset = $end;
  620. }
  621. else
  622. {
  623. return $default;
  624. }
  625. }
  626. if ($key)
  627. {
  628. unset($parent[$key]);
  629. }
  630. return $value;
  631. }
  632. /**
  633. * Get path to php cli.
  634. *
  635. * @throws sfException If no php cli found
  636. * @return string
  637. */
  638. public static function getPhpCli()
  639. {
  640. $path = getenv('PATH') ? getenv('PATH') : getenv('Path');
  641. $suffixes = DIRECTORY_SEPARATOR == '\\' ? (getenv('PATHEXT') ? explode(PATH_SEPARATOR, getenv('PATHEXT')) : array('.exe', '.bat', '.cmd', '.com')) : array('');
  642. foreach (array('php5', 'php') as $phpCli)
  643. {
  644. foreach ($suffixes as $suffix)
  645. {
  646. foreach (explode(PATH_SEPARATOR, $path) as $dir)
  647. {
  648. if (is_file($file = $dir.DIRECTORY_SEPARATOR.$phpCli.$suffix) && is_executable($file))
  649. {
  650. return $file;
  651. }
  652. }
  653. }
  654. }
  655. throw new sfException('Unable to find PHP executable.');
  656. }
  657. /**
  658. * From PEAR System.php
  659. *
  660. * LICENSE: This source file is subject to version 3.0 of the PHP license
  661. * that is available through the world-wide-web at the following URI:
  662. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  663. * the PHP License and are unable to obtain it through the web, please
  664. * send a note to license@php.net so we can mail you a copy immediately.
  665. *
  666. * @author Tomas V.V.Cox <cox@idecnet.com>
  667. * @copyright 1997-2006 The PHP Group
  668. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  669. */
  670. public static function getTmpDir()
  671. {
  672. if (DIRECTORY_SEPARATOR == '\\')
  673. {
  674. if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP'))
  675. {
  676. return $var;
  677. }
  678. if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP'))
  679. {
  680. return $var;
  681. }
  682. if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir'))
  683. {
  684. return $var;
  685. }
  686. return getenv('SystemRoot').'\temp';
  687. }
  688. if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR'))
  689. {
  690. return $var;
  691. }
  692. return '/tmp';
  693. }
  694. /**
  695. * Converts strings to UTF-8 via iconv. NB, the result may not by UTF-8 if the conversion failed.
  696. *
  697. * This file comes from Prado (BSD License)
  698. *
  699. * @param string $string string to convert to UTF-8
  700. * @param string $from current encoding
  701. *
  702. * @return string UTF-8 encoded string, original string if iconv failed.
  703. */
  704. static public function I18N_toUTF8($string, $from)
  705. {
  706. $from = strtoupper($from);
  707. if ($from != 'UTF-8')
  708. {
  709. $s = iconv($from,'UTF-8',$string); // to UTF-8
  710. return $s !== false ? $s : $string; // it could return false
  711. }
  712. return $string;
  713. }
  714. /**
  715. * Converts UTF-8 strings to a different encoding. NB. The result may not have been encoded if iconv fails.
  716. *
  717. * This file comes from Prado (BSD License)
  718. *
  719. * @param string $string the UTF-8 string for conversion
  720. * @param string $to new encoding
  721. *
  722. * @return string encoded string.
  723. */
  724. static public function I18N_toEncoding($string, $to)
  725. {
  726. $to = strtoupper($to);
  727. if ($to != 'UTF-8')
  728. {
  729. $s = iconv('UTF-8', $to, $string);
  730. return $s !== false ? $s : $string;
  731. }
  732. return $string;
  733. }
  734. }