PageRenderTime 49ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/core/src/main/php/lang/types/String.class.php

http://github.com/xp-framework/xp-framework
PHP | 344 lines | 161 code | 31 blank | 152 comment | 26 complexity | 1947c4903b30510a079e47697415a4a2 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /* This class is part of the XP framework
  3. *
  4. * $Id$
  5. */
  6. define('STR_ENC', 'utf-8');
  7. uses('lang.types.Character', 'lang.types.Bytes');
  8. if (extension_loaded('mbstring')) {
  9. mb_internal_encoding(STR_ENC);
  10. class __str {
  11. static function len($buf) { return mb_strlen($buf); }
  12. static function substr($buf, $start, $length) { return mb_substr($buf, $start, $length); }
  13. static function pos($buf, $needle, $start) { return mb_strpos($buf, $needle, $start); }
  14. static function rpos($buf, $needle) { return mb_strrpos($buf, $needle); }
  15. }
  16. } else {
  17. iconv_set_encoding('internal_encoding', STR_ENC);
  18. class __str {
  19. static function len($buf) { return iconv_strlen($buf); }
  20. static function substr($buf, $start, $length) { return iconv_substr($buf, $start, $length); }
  21. static function pos($buf, $needle, $start) { return iconv_strpos($buf, $needle, $start); }
  22. static function rpos($buf, $needle) { return iconv_strrpos($buf, $needle); }
  23. }
  24. }
  25. /**
  26. * Represents a string
  27. *
  28. * @ext iconv
  29. * @test xp://net.xp_framework.unittest.core.types.StringTest
  30. * @purpose Wrapper type
  31. */
  32. class String extends Object implements ArrayAccess {
  33. protected
  34. $buffer= '',
  35. $length= 0;
  36. public static $EMPTY = NULL;
  37. static function __static() {
  38. self::$EMPTY= new self('', STR_ENC);
  39. }
  40. /**
  41. * Convert a string to internal encoding
  42. *
  43. * @param string string
  44. * @param string charset default NULL
  45. * @return string
  46. * @throws lang.FormatException in case a conversion error occurs
  47. */
  48. protected function asIntern($arg, $charset= NULL) {
  49. if ($arg instanceof self) {
  50. return $arg->buffer;
  51. } else if ($arg instanceof Character) {
  52. return $arg->getBytes(STR_ENC)->buffer;
  53. } else {
  54. $charset= $charset ? strtolower($charset) : xp::ENCODING;
  55. // Convert the input to internal encoding
  56. $buffer= iconv($charset, STR_ENC, $arg);
  57. if (xp::errorAt(__FILE__, __LINE__ - 1)) {
  58. $message= key(xp::$errors[__FILE__][__LINE__ - 2]);
  59. xp::gc(__FILE__);
  60. throw new FormatException($message.($charset == STR_ENC
  61. ? ' with charset '.$charset
  62. : $message.' while converting input from '.$charset.' to '.STR_ENC
  63. ));
  64. }
  65. return $buffer;
  66. }
  67. }
  68. /**
  69. * Constructor
  70. *
  71. * @param string initial default ''
  72. * @param string charset default NULL
  73. */
  74. public function __construct($initial= '', $charset= NULL) {
  75. if (NULL === $initial) return;
  76. $this->buffer= $this->asIntern($initial, $charset);
  77. $this->length= __str::len($this->buffer);
  78. }
  79. /**
  80. * = list[] overloading
  81. *
  82. * @param int offset
  83. * @return lang.types.Character
  84. * @throws lang.IndexOutOfBoundsException if key does not exist
  85. */
  86. public function offsetGet($offset) {
  87. return $this->charAt($offset);
  88. }
  89. /**
  90. * list[]= overloading
  91. *
  92. * @param int offset
  93. * @param var value
  94. * @throws lang.IllegalArgumentException if key is neither numeric (set) nor NULL (add)
  95. */
  96. public function offsetSet($offset, $value) {
  97. if (!is_int($offset)) {
  98. throw new IllegalArgumentException('Incorrect type '.gettype($offset).' for index');
  99. }
  100. if ($offset >= $this->length || $offset < 0) {
  101. raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds');
  102. }
  103. $char= $this->asIntern($value);
  104. if (1 != __str::len($char)) {
  105. throw new IllegalArgumentException('Set only allows to set one character!');
  106. }
  107. $this->buffer= (
  108. __str::substr($this->buffer, 0, $offset).
  109. $char.
  110. __str::substr($this->buffer, $offset+ 1, $this->length)
  111. );
  112. }
  113. /**
  114. * isset() overloading
  115. *
  116. * @param int offset
  117. * @return bool
  118. */
  119. public function offsetExists($offset) {
  120. return ($offset >= 0 && $offset < $this->length);
  121. }
  122. /**
  123. * unset() overloading
  124. *
  125. * @param int offset
  126. */
  127. public function offsetUnset($offset) {
  128. if ($offset >= $this->length || $offset < 0) {
  129. raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds');
  130. }
  131. $this->buffer= (
  132. __str::substr($this->buffer, 0, $offset).
  133. __str::substr($this->buffer, $offset+ 1, $this->length)
  134. );
  135. $this->length= __str::len($this->buffer);
  136. }
  137. /**
  138. * Returns the string's length (the number of characters in this
  139. * string, not the number of bytes)
  140. *
  141. * @return string
  142. */
  143. public function length() {
  144. return $this->length;
  145. }
  146. /**
  147. * Returns the character at the given position
  148. *
  149. * @param int offset
  150. * @return lang.types.Character
  151. * @throws lang.IndexOutOfBoundsException if key does not exist
  152. */
  153. public function charAt($offset) {
  154. if ($offset >= $this->length || $offset < 0) {
  155. raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds');
  156. }
  157. return new Character(__str::substr($this->buffer, $offset, 1), STR_ENC);
  158. }
  159. /**
  160. * Returns the index within this string of the first occurrence of
  161. * the specified substring.
  162. *
  163. * @param var arg either a string or a String
  164. * @param int start default 0
  165. * @return bool
  166. */
  167. public function indexOf($arg, $start= 0) {
  168. if ('' === ($needle= $this->asIntern($arg))) return -1;
  169. $r= __str::pos($this->buffer, $needle, $start);
  170. return FALSE === $r ? -1 : $r;
  171. }
  172. /**
  173. * Returns the index within this string of the last occurrence of
  174. * the specified substring.
  175. *
  176. * @param var arg either a string or a String
  177. * @return bool
  178. */
  179. public function lastIndexOf($arg) {
  180. if ('' === ($needle= $this->asIntern($arg))) return -1;
  181. $r= __str::rpos($this->buffer, $needle);
  182. return FALSE === $r ? -1 : $r;
  183. }
  184. /**
  185. * Returns a new string that is a substring of this string.
  186. *
  187. * @param int start
  188. * @param int length default 0
  189. * @return lang.types.String
  190. */
  191. public function substring($start, $length= 0) {
  192. if (0 === $length) $length= $this->length;
  193. $self= new self(NULL);
  194. $self->buffer= __str::substr($this->buffer, $start, $length);
  195. $self->length= __str::len($self->buffer);
  196. return $self;
  197. }
  198. /**
  199. * Returns whether a given substring is contained in this string
  200. *
  201. * @param var arg
  202. * @return bool
  203. */
  204. public function contains($arg) {
  205. if ('' === ($needle= $this->asIntern($arg))) return FALSE;
  206. return FALSE !== __str::pos($this->buffer, $needle, 0);
  207. }
  208. /**
  209. * Returns whether a given substring is contained in this string
  210. *
  211. * @param var old
  212. * @param var new default ''
  213. * @return lang.types.String this string
  214. */
  215. public function replace($old, $new= '') {
  216. $this->buffer= str_replace($this->asIntern($old), $this->asIntern($new), $this->buffer);
  217. $this->length= __str::len($this->buffer);
  218. return $this;
  219. }
  220. /**
  221. * Concatenates the given argument to the end of this string and returns
  222. * this String so it can be used in chained calls:
  223. *
  224. * <code>
  225. * $s= new String('Hello');
  226. * $s->concat(' ')->concat('World');
  227. * </code>
  228. *
  229. * @param var arg
  230. * @return lang.types.String this string
  231. */
  232. public function concat($arg) {
  233. $this->buffer.= $this->asIntern($arg);
  234. $this->length= __str::len($this->buffer);
  235. return $this;
  236. }
  237. /**
  238. * Returns whether this string starts with a given argument.
  239. *
  240. * @param var arg either a string or a String
  241. * @return bool
  242. */
  243. public function startsWith($arg) {
  244. $bytes= $this->asIntern($arg);
  245. return 0 === strncmp($this->buffer, $bytes, strlen($bytes));
  246. }
  247. /**
  248. * Returns whether this string ends with a given argument.
  249. *
  250. * @param var arg either a string or a String
  251. * @return bool
  252. */
  253. public function endsWith($arg) {
  254. $bytes= $this->asIntern($arg);
  255. $l= strlen($bytes);
  256. return 0 === substr_compare($this->buffer, $bytes, -$l, $l);
  257. }
  258. /**
  259. * Returns whether a given object is equal to this object
  260. *
  261. * @param lang.Generic cmp
  262. * @return bool
  263. */
  264. public function equals($cmp) {
  265. return $cmp instanceof self && $this->buffer === $cmp->buffer;
  266. }
  267. /**
  268. * Returns a hashcode for this string object
  269. *
  270. * @return string
  271. */
  272. public function hashCode() {
  273. return md5($this->buffer);
  274. }
  275. /**
  276. * Returns a string representation of this string. Uses the current
  277. * output encoding and transliteration.
  278. *
  279. * @return string
  280. */
  281. public function toString() {
  282. return iconv(STR_ENC, xp::ENCODING.'//TRANSLIT', $this->buffer);
  283. }
  284. /**
  285. * Returns a string representation of this string. Uses the current
  286. * output encoding and transliteration.
  287. *
  288. * @return string
  289. */
  290. public function __toString() {
  291. return iconv(STR_ENC, xp::ENCODING.'//TRANSLIT', $this->buffer);
  292. }
  293. /**
  294. * Returns the bytes representing this string
  295. *
  296. * @param string charset default NULL
  297. * @return lang.types.Bytes
  298. */
  299. public function getBytes($charset= NULL) {
  300. $charset= $charset ? strtolower($charset) : xp::ENCODING;
  301. if (STR_ENC === $charset) {
  302. return new Bytes($this->buffer);
  303. }
  304. $bytes= iconv(STR_ENC, $charset, $this->buffer);
  305. if (xp::errorAt(__FILE__, __LINE__ - 1)) {
  306. $message= key(xp::$errors[__FILE__][__LINE__ - 2]);
  307. xp::gc(__FILE__);
  308. throw new FormatException($message.' while converting input from '.STR_ENC.' to '.$charset);
  309. }
  310. return new Bytes($bytes);
  311. }
  312. }
  313. ?>