1. sfToolkit.class.php
  2. /** * sfToolkit provides basic utility methods. * * @package symfony * @subpackage util * @author Fabien Potencier * @author Sean Kerr * @version SVN: $Id: sfToolkit.class.php 23945 2009-11-14 18:12:41Z fabien $ */
  3. class sfToolkit
  4. {
  5. /**
  6. * Extract the class or interface name from filename.
  7. *
  8. * @param string $filename A filename.
  9. *
  10. * @return string A class or interface name, if one can be extracted, otherwise null.
  11. */
  12. public static function extractClassName($filename)
  13. {
  14. $retval = null;
  15. if (self::isPathAbsolute($filename))
  16. {
  17. $filename = basename($filename);
  18. }
  19. $pattern = '/(.*?)\.(class|interface)\.php/i';
  20. if (preg_match($pattern, $filename, $match))
  21. {
  22. $retval = $match[1];
  23. }
  24. return $retval;
  25. }
  26. /**
  27. * Clear all files in a given directory.
  28. *
  29. * @param string $directory An absolute filesystem path to a directory.
  30. */
  31. public static function clearDirectory($directory)
  32. {
  33. if (!is_dir($directory))
  34. {
  35. return;
  36. }
  37. // open a file point to the cache dir
  38. $fp = opendir($directory);
  39. // ignore names
  40. $ignore = array('.', '..', 'CVS', '.svn');
  41. while (($file = readdir($fp)) !== false)
  42. {
  43. if (!in_array($file, $ignore))
  44. {
  45. if (is_link($directory.'/'.$file))
  46. {
  47. // delete symlink
  48. unlink($directory.'/'.$file);
  49. }
  50. else if (is_dir($directory.'/'.$file))
  51. {
  52. // recurse through directory
  53. self::clearDirectory($directory.'/'.$file);
  54. // delete the directory
  55. rmdir($directory.'/'.$file);
  56. }
  57. else
  58. {
  59. // delete the file
  60. unlink($directory.'/'.$file);
  61. }
  62. }
  63. }
  64. // close file pointer
  65. closedir($fp);
  66. }
  67. /**
  68. * Clear all files and directories corresponding to a glob pattern.
  69. *
  70. * @param string $pattern An absolute filesystem pattern.
  71. */
  72. public static function clearGlob($pattern)
  73. {
  74. $files = glob($pattern);
  75. // order is important when removing directories
  76. sort($files);
  77. foreach ($files as $file)
  78. {
  79. if (is_dir($file))
  80. {
  81. // delete directory
  82. self::clearDirectory($file);
  83. }
  84. else
  85. {
  86. // delete file
  87. unlink($file);
  88. }
  89. }
  90. }
  91. /**
  92. * Determine if a filesystem path is absolute.
  93. *
  94. * @param path $path A filesystem path.
  95. *
  96. * @return bool true, if the path is absolute, otherwise false.
  97. */
  98. public static function isPathAbsolute($path)
  99. {
  100. if ($path[0] == '/' || $path[0] == '\\' ||
  101. (strlen($path) > 3 && ctype_alpha($path[0]) &&
  102. $path[1] == ':' &&
  103. ($path[2] == '\\' || $path[2] == '/')
  104. )
  105. )
  106. {
  107. return true;
  108. }
  109. return false;
  110. }
  111. /**
  112. * Strips comments from php source code
  113. *
  114. * @param string $source PHP source code.
  115. *
  116. * @return string Comment free source code.
  117. */
  118. public static function stripComments($source)
  119. {
  120. if (!function_exists('token_get_all'))
  121. {
  122. return $source;
  123. }
  124. $ignore = array(T_COMMENT => true, T_DOC_COMMENT => true);
  125. $output = '';
  126. foreach (token_get_all($source) as $token)
  127. {
  128. // array
  129. if (isset($token[1]))
  130. {
  131. // no action on comments
  132. if (!isset($ignore[$token[0]]))
  133. {
  134. // anything else -> output "as is"
  135. $output .= $token[1];
  136. }
  137. }
  138. else
  139. {
  140. // simple 1-character token
  141. $output .= $token;
  142. }
  143. }
  144. return $output;
  145. }
  146. /**
  147. * Strip slashes recursively from array
  148. *
  149. * @param array $value the value to strip
  150. *
  151. * @return array clean value with slashes stripped
  152. */
  153. public static function stripslashesDeep($value)
  154. {
  155. return is_array($value) ? array_map(array('sfToolkit', 'stripslashesDeep'), $value) : stripslashes($value);
  156. }
  157. // code from php at moechofe dot com (array_merge comment on php.net)
  158. /*
  159. * array arrayDeepMerge ( array array1 [, array array2 [, array ...]] )
  160. *
  161. * Like array_merge
  162. *
  163. * arrayDeepMerge() merges the elements of one or more arrays together so
  164. * that the values of one are appended to the end of the previous one. It
  165. * returns the resulting array.
  166. * If the input arrays have the same string keys, then the later value for
  167. * that key will overwrite the previous one. If, however, the arrays contain
  168. * numeric keys, the later value will not overwrite the original value, but
  169. * will be appended.
  170. * If only one array is given and the array is numerically indexed, the keys
  171. * get reindexed in a continuous way.
  172. *
  173. * Different from array_merge
  174. * If string keys have arrays for values, these arrays will merge recursively.
  175. */
  176. public static function arrayDeepMerge()
  177. {
  178. switch (func_num_args())
  179. {
  180. case 0:
  181. return false;
  182. case 1:
  183. return func_get_arg(0);
  184. case 2:
  185. $args = func_get_args();
  186. $args[2] = array();
  187. if (is_array($args[0]) && is_array($args[1]))
  188. {
  189. foreach (array_unique(array_merge(array_keys($args[0]),array_keys($args[1]))) as $key)
  190. {
  191. $isKey0 = array_key_exists($key, $args[0]);
  192. $isKey1 = array_key_exists($key, $args[1]);
  193. if ($isKey0 && $isKey1 && is_array($args[0][$key]) && is_array($args[1][$key]))
  194. {
  195. $args[2][$key] = self::arrayDeepMerge($args[0][$key], $args[1][$key]);
  196. }
  197. else if ($isKey0 && $isKey1)
  198. {
  199. $args[2][$key] = $args[1][$key];
  200. }
  201. else if (!$isKey1)
  202. {
  203. $args[2][$key] = $args[0][$key];
  204. }
  205. else if (!$isKey0)
  206. {
  207. $args[2][$key] = $args[1][$key];
  208. }
  209. }
  210. return $args[2];
  211. }
  212. else
  213. {
  214. return $args[1];
  215. }
  216. default :
  217. $args = func_get_args();
  218. $args[1] = sfToolkit::arrayDeepMerge($args[0], $args[1]);
  219. array_shift($args);
  220. return call_user_func_array(array('sfToolkit', 'arrayDeepMerge'), $args);
  221. break;
  222. }
  223. }
  224. /**
  225. * Converts string to array
  226. *
  227. * @param string $string the value to convert to array
  228. *
  229. * @return array
  230. */
  231. public static function stringToArray($string)
  232. {
  233. preg_match_all('/
  234. \s*(\w+) # key \\1
  235. \s*=\s* # =
  236. (\'|")? # values may be included in \' or " \\2
  237. (.*?) # value \\3
  238. (?(2) \\2) # matching \' or " if needed \\4
  239. \s*(?:
  240. (?=\w+\s*=) | \s*$ # followed by another key= or the end of the string
  241. )
  242. /x', $string, $matches, PREG_SET_ORDER);
  243. $attributes = array();
  244. foreach ($matches as $val)
  245. {
  246. $attributes[$val[1]] = self::literalize($val[3]);
  247. }
  248. return $attributes;
  249. }
  250. /**
  251. * Finds the type of the passed value, returns the value as the new type.
  252. *
  253. * @param string $value
  254. * @param bool $quoted Quote?
  255. *
  256. * @return mixed
  257. */
  258. public static function literalize($value, $quoted = false)
  259. {
  260. // lowercase our value for comparison
  261. $value = trim($value);
  262. $lvalue = strtolower($value);
  263. if (in_array($lvalue, array('null', '~', '')))
  264. {
  265. $value = null;
  266. }
  267. else if (in_array($lvalue, array('true', 'on', '+', 'yes')))
  268. {
  269. $value = true;
  270. }
  271. else if (in_array($lvalue, array('false', 'off', '-', 'no')))
  272. {
  273. $value = false;
  274. }
  275. else if (ctype_digit($value))
  276. {
  277. $value = (int) $value;
  278. }
  279. else if (is_numeric($value))
  280. {
  281. $value = (float) $value;
  282. }
  283. else
  284. {
  285. $value = self::replaceConstants($value);
  286. if ($quoted)
  287. {
  288. $value = '\''.str_replace('\'', '\\\'', $value).'\'';
  289. }
  290. }
  291. return $value;
  292. }
  293. /**
  294. * Replaces constant identifiers in a scalar value.
  295. *
  296. * @param string $value the value to perform the replacement on
  297. *
  298. * @return string the value with substitutions made
  299. */
  300. public static function replaceConstants($value)
  301. {
  302. 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;
  303. }
  304. /**
  305. * Returns subject replaced with regular expression matchs
  306. *
  307. * @param mixed $search subject to search
  308. * @param array $replacePairs array of search => replace pairs
  309. */
  310. public static function pregtr($search, $replacePairs)
  311. {
  312. return preg_replace(array_keys($replacePairs), array_values($replacePairs), $search);
  313. }
  314. /**
  315. * Checks if array values are empty
  316. *
  317. * @param array $array the array to check
  318. * @return boolean true if empty, otherwise false
  319. */
  320. public static function isArrayValuesEmpty($array)
  321. {
  322. static $isEmpty = true;
  323. foreach ($array as $value)
  324. {
  325. $isEmpty = (is_array($value)) ? self::isArrayValuesEmpty($value) : (strlen($value) == 0);
  326. if (!$isEmpty)
  327. {
  328. break;
  329. }
  330. }
  331. return $isEmpty;
  332. }
  333. /**
  334. * Checks if a string is an utf8.
  335. *
  336. * Yi Stone Li<yili@yahoo-inc.com>
  337. * Copyright (c) 2007 Yahoo! Inc. All rights reserved.
  338. * Licensed under the BSD open source license
  339. *
  340. * @param string
  341. *
  342. * @return bool true if $string is valid UTF-8 and false otherwise.
  343. */
  344. public static function isUTF8($string)
  345. {
  346. for ($idx = 0, $strlen = strlen($string); $idx < $strlen; $idx++)
  347. {
  348. $byte = ord($string[$idx]);
  349. if ($byte & 0x80)
  350. {
  351. if (($byte & 0xE0) == 0xC0)
  352. {
  353. // 2 byte char
  354. $bytes_remaining = 1;
  355. }
  356. else if (($byte & 0xF0) == 0xE0)
  357. {
  358. // 3 byte char
  359. $bytes_remaining = 2;
  360. }
  361. else if (($byte & 0xF8) == 0xF0)
  362. {
  363. // 4 byte char
  364. $bytes_remaining = 3;
  365. }
  366. else
  367. {
  368. return false;
  369. }
  370. if ($idx + $bytes_remaining >= $strlen)
  371. {
  372. return false;
  373. }
  374. while ($bytes_remaining--)
  375. {
  376. if ((ord($string[++$idx]) & 0xC0) != 0x80)
  377. {
  378. return false;
  379. }
  380. }
  381. }
  382. }
  383. return true;
  384. }
  385. /**
  386. * Returns an array value for a path.
  387. *
  388. * @param array $values The values to search
  389. * @param string $name The token name
  390. * @param array $default Default if not found
  391. *
  392. * @return array
  393. */
  394. public static function getArrayValueForPath($values, $name, $default = null)
  395. {
  396. if (false === $offset = strpos($name, '['))
  397. {
  398. return isset($values[$name]) ? $values[$name] : $default;
  399. }
  400. if (!isset($values[substr($name, 0, $offset)]))
  401. {
  402. return $default;
  403. }
  404. $array = $values[substr($name, 0, $offset)];
  405. while (false !== $pos = strpos($name, '[', $offset))
  406. {
  407. $end = strpos($name, ']', $pos);
  408. if ($end == $pos + 1)
  409. {
  410. // reached a []
  411. if (!is_array($array))
  412. {
  413. return $default;
  414. }
  415. break;
  416. }
  417. else if (!isset($array[substr($name, $pos + 1, $end - $pos - 1)]))
  418. {
  419. return $default;
  420. }
  421. else if (is_array($array))
  422. {
  423. $array = $array[substr($name, $pos + 1, $end - $pos - 1)];
  424. $offset = $end;
  425. }
  426. else
  427. {
  428. return $default;
  429. }
  430. }
  431. return $array;
  432. }
  433. /**
  434. * Get path to php cli.
  435. *
  436. * @throws sfException If no php cli found
  437. * @return string
  438. */
  439. public static function getPhpCli()
  440. {
  441. $path = getenv('PATH') ? getenv('PATH') : getenv('Path');
  442. $suffixes = DIRECTORY_SEPARATOR == '\\' ? (getenv('PATHEXT') ? explode(PATH_SEPARATOR, getenv('PATHEXT')) : array('.exe', '.bat', '.cmd', '.com')) : array('');
  443. foreach (array('php5', 'php') as $phpCli)
  444. {
  445. foreach ($suffixes as $suffix)
  446. {
  447. foreach (explode(PATH_SEPARATOR, $path) as $dir)
  448. {
  449. if (is_file($file = $dir.DIRECTORY_SEPARATOR.$phpCli.$suffix) && is_executable($file))
  450. {
  451. return $file;
  452. }
  453. }
  454. }
  455. }
  456. throw new sfException('Unable to find PHP executable.');
  457. }
  458. /**
  459. * Converts strings to UTF-8 via iconv. NB, the result may not by UTF-8 if the conversion failed.
  460. *
  461. * This file comes from Prado (BSD License)
  462. *
  463. * @param string $string string to convert to UTF-8
  464. * @param string $from current encoding
  465. *
  466. * @return string UTF-8 encoded string, original string if iconv failed.
  467. */
  468. static public function I18N_toUTF8($string, $from)
  469. {
  470. $from = strtoupper($from);
  471. if ($from != 'UTF-8')
  472. {
  473. $s = iconv($from,'UTF-8',$string); // to UTF-8
  474. return $s !== false ? $s : $string; // it could return false
  475. }
  476. return $string;
  477. }
  478. /**
  479. * Converts UTF-8 strings to a different encoding. NB. The result may not have been encoded if iconv fails.
  480. *
  481. * This file comes from Prado (BSD License)
  482. *
  483. * @param string $string the UTF-8 string for conversion
  484. * @param string $to new encoding
  485. *
  486. * @return string encoded string.
  487. */
  488. static public function I18N_toEncoding($string, $to)
  489. {
  490. $to = strtoupper($to);
  491. if ($to != 'UTF-8')
  492. {
  493. $s = iconv('UTF-8', $to, $string);
  494. return $s !== false ? $s : $string;
  495. }
  496. return $string;
  497. }
  498. /**
  499. * Adds a path to the PHP include_path setting.
  500. *
  501. * @param mixed $path Single string path or an array of paths
  502. * @param string $position Either 'front' or 'back'
  503. *
  504. * @return string The old include path
  505. */
  506. static public function addIncludePath($path, $position = 'front')
  507. {
  508. if (is_array($path))
  509. {
  510. foreach ('front' == $position ? array_reverse($path) : $path as $p)
  511. {
  512. self::addIncludePath($p, $position);
  513. }
  514. return;
  515. }
  516. $paths = explode(PATH_SEPARATOR, get_include_path());
  517. // remove what's already in the include_path
  518. if (false !== $key = array_search(realpath($path), array_map('realpath', $paths)))
  519. {
  520. unset($paths[$key]);
  521. }
  522. switch ($position)
  523. {
  524. case 'front':
  525. array_unshift($paths, $path);
  526. break;
  527. case 'back':
  528. $paths[] = $path;
  529. break;
  530. default:
  531. throw new InvalidArgumentException(sprintf('Unrecognized position: "%s"', $position));
  532. }
  533. return set_include_path(join(PATH_SEPARATOR, $paths));
  534. }
  535. }

Debug toolbar