PageRenderTime 30ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/htdocs/lithium/0.9.9/libraries/lithium/util/String.php

https://github.com/ad2joe/php-framework-benchmarks
PHP | 374 lines | 238 code | 32 blank | 104 comment | 59 complexity | ae18dca140bc5c58b8c0df3611d612a1 MD5 | raw file
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org)
  6. * Copyright 2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  7. * @license http://opensource.org/licenses/mit-license.php The MIT License
  8. */
  9. namespace lithium\util;
  10. use \Closure;
  11. use \Exception;
  12. /**
  13. * String manipulation utility class. Includes functionality for hashing, UUID generation,
  14. * {:tag} and regex replacement, and tokenization.
  15. *
  16. */
  17. class String {
  18. /**
  19. * Generates a random UUID.
  20. *
  21. * @param mixed $context Used to determine the values for `'SERVER_ADDR'`, `'HOST'`
  22. * and `'HOSTNAME'`. Either a closure which is passed the requested context values, an
  23. * object with properties for each value or an array keyed by requested context value.
  24. * @return string An RFC 4122-compliant UUID.
  25. * @link http://www.ietf.org/rfc/rfc4122.txt
  26. */
  27. public static function uuid($context) {
  28. $val = function($value) use ($context) {
  29. switch (true) {
  30. case is_object($context) && is_callable($context):
  31. $result = $context($value);
  32. break;
  33. case is_object($context):
  34. $result = isset($context->$value) ? $context->$value : null;
  35. break;
  36. case is_array($context):
  37. $result = isset($context[$value]) ? $context[$value] : null;
  38. break;
  39. }
  40. return $result;
  41. };
  42. $node = static::_hostname($val);
  43. $pid = function_exists('zend_thread_id') ? zend_thread_id() : getmypid();
  44. $pid = (!$pid || $pid > 65535) ? mt_rand(0, 0xfff) | 0x4000 : $pid;
  45. list($timeMid, $timeLow) = explode(' ', microtime());
  46. return sprintf(
  47. "%08x-%04x-%04x-%02x%02x-%04x%08x",
  48. (integer) $timeLow, (integer) substr($timeMid, 2) & 0xffff, mt_rand(0, 0xfff) | 0x4000,
  49. mt_rand(0, 0x3f) | 0x80, mt_rand(0, 0xff), $pid, $node
  50. );
  51. }
  52. /**
  53. * Create a hash from string using given method. Fallback on next available method.
  54. *
  55. * @param string $string String to hash.
  56. * @param string $type Method to use (sha1/sha256/md5, or any method supported
  57. * by the `hash()` function).
  58. * @param string $salt
  59. * @return string Hash.
  60. */
  61. public static function hash($string, $type = 'sha1', $salt = null) {
  62. $string = $salt . $string;
  63. switch (true) {
  64. case ($type == 'sha1' && function_exists('sha1')):
  65. return sha1($string);
  66. case ($type == 'sha256' && function_exists('mhash')):
  67. return bin2hex(mhash(MHASH_SHA256, $string));
  68. case (function_exists('hash')):
  69. return hash($type, $string);
  70. default:
  71. }
  72. return md5($string);
  73. }
  74. /**
  75. * Replaces variable placeholders inside a string with any given data. Each key
  76. * in the `$data` array corresponds to a variable placeholder name in `$str`.
  77. *
  78. * Usage:
  79. * {{{
  80. * String::insert(
  81. * 'My name is {:name} and I am {:age} years old.',
  82. * array('name' => 'Bob', 'age' => '65')
  83. * ); // returns 'My name is Bob and I am 65 years old.'
  84. * }}}
  85. *
  86. * @param string $str A string containing variable place-holders.
  87. * @param string $data A key, value array where each key stands for a place-holder variable
  88. * name to be replaced with value.
  89. * @param string $options Available options are:
  90. * - `'after'`: The character or string after the name of the variable place-holder
  91. * (defaults to `null`).
  92. * - `'before'`: The character or string in front of the name of the variable
  93. * place-holder (defaults to `':'`).
  94. * - `'clean'`: A boolean or array with instructions for `String::clean()`.
  95. * - `'escape'`: The character or string used to escape the before character or string
  96. * (defaults to `'\'`).
  97. * - `'format'`: A regular expression to use for matching variable place-holders
  98. * (defaults to `'/(?<!\\)\:%s/'`. Please note that this option takes precedence over
  99. * all other options except `'clean'`.
  100. * @return string
  101. * @todo Optimize this
  102. */
  103. public static function insert($str, array $data, array $options = array()) {
  104. $defaults = array(
  105. 'before' => '{:',
  106. 'after' => '}',
  107. 'escape' => null,
  108. 'format' => null,
  109. 'clean' => false,
  110. );
  111. $options += $defaults;
  112. $format = $options['format'];
  113. reset($data);
  114. if ($format == 'regex' || (empty($format) && !empty($options['escape']))) {
  115. $format = sprintf(
  116. '/(?<!%s)%s%%s%s/',
  117. preg_quote($options['escape'], '/'),
  118. str_replace('%', '%%', preg_quote($options['before'], '/')),
  119. str_replace('%', '%%', preg_quote($options['after'], '/'))
  120. );
  121. }
  122. if (empty($format) && key($data) !== 0) {
  123. $replace = array();
  124. foreach ($data as $key => $value) {
  125. $replace["{$options['before']}{$key}{$options['after']}"] = $value;
  126. }
  127. $str = strtr($str, $replace);
  128. return $options['clean'] ? static::clean($str, $options) : $str;
  129. }
  130. if (strpos($str, '?') !== false && isset($data[0])) {
  131. $offset = 0;
  132. while (($pos = strpos($str, '?', $offset)) !== false) {
  133. $val = array_shift($data);
  134. $offset = $pos + strlen($val);
  135. $str = substr_replace($str, $val, $pos, 1);
  136. }
  137. return $options['clean'] ? static::clean($str, $options) : $str;
  138. }
  139. foreach ($data as $key => $value) {
  140. $hashVal = crc32($key);
  141. $key = sprintf($format, preg_quote($key, '/'));
  142. if (!$key) {
  143. continue;
  144. }
  145. $str = preg_replace($key, $hashVal, $str);
  146. if (is_object($value) && !$value instanceof Closure) {
  147. try {
  148. $value = $value->__toString();
  149. } catch (Exception $e) {
  150. $value = '';
  151. }
  152. }
  153. if (!is_array($value)) {
  154. $str = str_replace($hashVal, $value, $str);
  155. }
  156. }
  157. if (!isset($options['format']) && isset($options['before'])) {
  158. $str = str_replace($options['escape'] . $options['before'], $options['before'], $str);
  159. }
  160. return $options['clean'] ? static::clean($str, $options) : $str;
  161. }
  162. /**
  163. * Cleans up a `Set::insert()` formatted string with given `$options` depending
  164. * on the `'clean'` option. The goal of this function is to replace all whitespace
  165. * and unneeded mark-up around place-holders that did not get replaced by `Set::insert()`.
  166. *
  167. * @param string $str The string to clean.
  168. * @param string $options Available options are:
  169. * - `'after'`: characters marking the end of targeted substring.
  170. * - `'andText'`: (defaults to `true`).
  171. * - `'before'`: characters marking the start of targeted substring.
  172. * - `'clean'`: `true` or an array of clean options:
  173. * - `'gap'`: Regular expression matching gaps.
  174. * - `'method'`: Either `'text'` or `'html'` (defaults to `'text'`).
  175. * - `'replacement'`: String to use for cleaned substrings (defaults to `''`).
  176. * - `'word'`: Regular expression matching words.
  177. * @return string The cleaned string.
  178. */
  179. public static function clean($str, array $options = array()) {
  180. if (!$options['clean']) {
  181. return $str;
  182. }
  183. $clean = $options['clean'];
  184. $clean = ($clean === true) ? array('method' => 'text') : $clean;
  185. $clean = (!is_array($clean)) ? array('method' => $options['clean']) : $clean;
  186. switch ($clean['method']) {
  187. case 'html':
  188. $clean += array('word' => '[\w,.]+', 'andText' => true, 'replacement' => '');
  189. $kleenex = sprintf(
  190. '/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i',
  191. preg_quote($options['before'], '/'),
  192. $clean['word'],
  193. preg_quote($options['after'], '/')
  194. );
  195. $str = preg_replace($kleenex, $clean['replacement'], $str);
  196. if ($clean['andText']) {
  197. $options['clean'] = array('method' => 'text');
  198. $str = static::clean($str, $options);
  199. }
  200. break;
  201. case 'text':
  202. $clean += array(
  203. 'word' => '[\w,.]+', 'gap' => '[\s]*(?:(?:and|or|,)[\s]*)?', 'replacement' => ''
  204. );
  205. $before = preg_quote($options['before'], '/');
  206. $after = preg_quote($options['after'], '/');
  207. $kleenex = sprintf(
  208. '/(%s%s%s%s|%s%s%s%s|%s%s%s%s%s)/',
  209. $before, $clean['word'], $after, $clean['gap'],
  210. $clean['gap'], $before, $clean['word'], $after,
  211. $clean['gap'], $before, $clean['word'], $after, $clean['gap']
  212. );
  213. $str = preg_replace($kleenex, $clean['replacement'], $str);
  214. break;
  215. }
  216. return $str;
  217. }
  218. /**
  219. * Extract a part of a string based on a regular expression `$regex`.
  220. *
  221. * @param string $regex The regular expression to use.
  222. * @param string $str The string to run the extraction on.
  223. * @param integer $index The number of the part to return based on the regex.
  224. * @return mixed
  225. */
  226. public static function extract($regex, $str, $index = 0) {
  227. if (!preg_match($regex, $str, $match)) {
  228. return false;
  229. }
  230. return isset($match[$index]) ? $match[$index] : null;
  231. }
  232. /**
  233. * Tokenizes a string using `$options['separator']`, ignoring any instance of
  234. * `$options['separator']` that appears between `$options['leftBound']` and
  235. * `$options['rightBound']`.
  236. *
  237. * @param string $data The data to tokenize.
  238. * @param array $options Options to use when tokenizing:
  239. * -`'separator'` _string_: The token to split the data on.
  240. * -`'leftBound'` _string_: Left scope-enclosing boundary.
  241. * -`'rightBound'` _string_: Right scope-enclosing boundary.
  242. * @return array Returns an array of tokens.
  243. */
  244. public static function tokenize($data, array $options = array()) {
  245. $defaults = array('separator' => ',', 'leftBound' => '(', 'rightBound' => ')');
  246. extract($options + $defaults);
  247. if (empty($data) || is_array($data)) {
  248. return $data;
  249. }
  250. $depth = 0;
  251. $offset = 0;
  252. $buffer = '';
  253. $results = array();
  254. $length = strlen($data);
  255. $open = false;
  256. while ($offset <= $length) {
  257. $tmpOffset = -1;
  258. $offsets = array(
  259. strpos($data, $separator, $offset),
  260. strpos($data, $leftBound, $offset),
  261. strpos($data, $rightBound, $offset)
  262. );
  263. for ($i = 0; $i < 3; $i++) {
  264. if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset == -1)) {
  265. $tmpOffset = $offsets[$i];
  266. }
  267. }
  268. if ($tmpOffset !== -1) {
  269. $buffer .= substr($data, $offset, ($tmpOffset - $offset));
  270. if ($data{$tmpOffset} == $separator && $depth == 0) {
  271. $results[] = $buffer;
  272. $buffer = '';
  273. } else {
  274. $buffer .= $data{$tmpOffset};
  275. }
  276. if ($leftBound != $rightBound) {
  277. if ($data{$tmpOffset} == $leftBound) {
  278. $depth++;
  279. }
  280. if ($data{$tmpOffset} == $rightBound) {
  281. $depth--;
  282. }
  283. } else {
  284. if ($data{$tmpOffset} == $leftBound) {
  285. if (!$open) {
  286. $depth++;
  287. $open = true;
  288. } else {
  289. $depth--;
  290. $open = false;
  291. }
  292. }
  293. }
  294. $offset = ++$tmpOffset;
  295. } else {
  296. $results[] = $buffer . substr($data, $offset);
  297. $offset = $length + 1;
  298. }
  299. }
  300. if (empty($results) && !empty($buffer)) {
  301. $results[] = $buffer;
  302. }
  303. return empty($results) ? array() : array_map('trim', $results);
  304. }
  305. /**
  306. * Used by `String::uuid()` to get the hostname from request context data. Uses fallbacks to get
  307. * the current host name or IP, depending on what values are available.
  308. *
  309. * @param mixed $context An array (i.e. `$_SERVER`), `Request` object, or anonymous function
  310. * containing host data.
  311. * @return string Returns the host name or IP for use in generating a UUID.
  312. */
  313. protected static function _hostname($context) {
  314. $node = $context('SERVER_ADDR');
  315. if (strpos($node, ':') !== false) {
  316. if (substr_count($node, '::')) {
  317. $pad = str_repeat(':0000', 8 - substr_count($node, ':'));
  318. $node = str_replace('::', $pad . ':', $node);
  319. }
  320. $node = explode(':', $node);
  321. $ipv6 = '';
  322. foreach ($node as $id) {
  323. $ipv6 .= str_pad(base_convert($id, 16, 2), 16, 0, STR_PAD_LEFT);
  324. }
  325. $node = base_convert($ipv6, 2, 10);
  326. $node = (strlen($node) < 38) ? null : crc32($node);
  327. } elseif (empty($node)) {
  328. $host = $context('HOSTNAME');
  329. $host = $host ?: $context('HOST');
  330. if (!empty($host)) {
  331. $ip = gethostbyname($host);
  332. $node = ($ip === $host) ? crc32($host) : $node = ip2long($ip);
  333. }
  334. } elseif ($node !== '127.0.0.1') {
  335. $node = ip2long($node);
  336. } else {
  337. $node = crc32(rand());
  338. }
  339. return $node;
  340. }
  341. }
  342. ?>