Skip to content

Instantly share code, notes, and snippets.

@ItsShadowl
Created September 12, 2019 18:38
Show Gist options
  • Save ItsShadowl/286131a0a42eba528b802263e598d0fd to your computer and use it in GitHub Desktop.
Save ItsShadowl/286131a0a42eba528b802263e598d0fd to your computer and use it in GitHub Desktop.
String class in PHP
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Stringie;
use \Exception\RuntimeException;
use \Exception\InvalidArgumentException;
/**
* @author Nicolas Grekas <p@tchwork.com>
* @author Hugo Hamon <hugohamon@neuf.fr>
* @author Olumide Samson <noobshow@gmail.com>
*
* @throws \Exception\InvalidArgumentException
* @throws \Exception\RuntimeException
*/
class Stringie
{
public function __construct(string $string = '', string $locale = null)
{
$this->string = $string;
if (null !== $locale) {
$this->locale = $this->withLocale($locale)->locale;
}
}
public static function random(int $length = 16): self
{
$string = '';
do {
$string .= str_replace(['/', '+', '='], '', base64_encode(random_bytes($length)));
} while (\strlen($string) < $length);
return new static(substr($string, 0, $length));
}
public function after(string $needle, bool $includeNeedle = false): self
{
$str = clone $this;
if ('' === $needle) {
$str->string = '';
} else {
$i = $this->ignoreCase ? stripos($this->string, $needle) : strpos($this->string, $needle);
$str->string = false === $i ? '' : substr($str->string, $i + ($includeNeedle ? 0 : \strlen($needle)));
}
return $str;
}
public function afterLast(string $needle, bool $includeNeedle = false): self
{
$str = clone $this;
if ('' === $needle) {
$str->string = '';
} else {
$i = $this->ignoreCase ? strripos($this->string, $needle) : strrpos($this->string, $needle);
$str->string = false === $i ? '' : substr($str->string, $i + ($includeNeedle ? 0 : \strlen($needle)));
}
return $str;
}
public function append(string ...$suffix): self
{
$str = clone $this;
$str->string .= 1 >= \count($suffix) ? $suffix[0] ?? '' : implode('', $suffix);
return $str;
}
public function before(string $needle, bool $includeNeedle = false): self
{
$str = clone $this;
if ('' === $needle) {
$str->string = '';
} else {
$i = $this->ignoreCase ? stripos($this->string, $needle) : strpos($this->string, $needle);
$str->string = false === $i ? '' : substr($str->string, 0, $i + ($includeNeedle ? \strlen($needle) : 0));
}
return $str;
}
public function beforeLast(string $needle, bool $includeNeedle = false): self
{
$str = clone $this;
if ('' === $needle) {
$str->string = '';
} else {
$i = $this->ignoreCase ? strripos($this->string, $needle) : strrpos($this->string, $needle);
$str->string = false === $i ? '' : substr($str->string, 0, $i + ($includeNeedle ? \strlen($needle) : 0));
}
return $str;
}
public function camel(bool $lcFirst): self
{
$str = clone $this;
$str->string = str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->string)));
if ($lcFirst) {
$str->string = lcfirst($str->string);
}
return $str;
}
public function chunk(int $length = 1): array
{
if (1 > $length) {
throw new \InvalidArgumentException('The maximum length of each segment must be greater than zero.');
}
$chunks = [];
if ('' === $this->string) {
return [];
}
foreach (str_split($this->string, $length) as $chunk) {
$chunks[] = $str = clone $this;
$str->string = $chunk;
}
return $chunks;
}
public function endsWith(string $suffix): bool
{
if ('' === $suffix) {
return true;
}
return \strlen($this->string) - \strlen($suffix) === ($this->ignoreCase ? strripos($this->string, $suffix) : strrpos($this->string, $suffix));
}
public function equalsTo(string $string): bool
{
if ($this->ignoreCase) {
return 0 === strcasecmp($string, $this->string);
}
return $string === $this->string;
}
public function folded(): self
{
$str = clone $this;
$str->string = strtolower($str->string);
return $str;
}
public function indexOf(string $needle, int $offset = 0): ?int
{
if ('' === $needle) {
return null;
}
$i = $this->ignoreCase ? stripos($this->string, $needle, $offset) : strpos($this->string, $needle, $offset);
return false === $i ? null : $i;
}
public function indexOfLast(string $needle, int $offset = 0): ?int
{
if ('' === $needle) {
return null;
}
$i = $this->ignoreCase ? strripos($this->string, $needle, $offset) : strrpos($this->string, $needle, $offset);
return false === $i ? null : $i;
}
public function join(array $strings): self
{
$str = clone $this;
$str->string = implode($str->string, $strings);
return $str;
}
public function length(): int
{
return \strlen($this->string);
}
public function lower(): self
{
$str = clone $this;
$str->string = strtolower($str->string);
return $str;
}
public function match(string $pattern, int $flags = 0, int $offset = 0): ?array
{
if ('g' === $pattern[-1]) {
$pattern = substr($pattern, 0, -1);
$match = 'preg_match_all';
} else {
$match = 'preg_match';
}
if ($this->ignoreCase) {
$pattern .= 'i';
}
set_error_handler(static function ($t, $m) { throw new \InvalidArgumentException($m); });
try {
if (false === $ret = $match($pattern, $this->string, $matches, $flags, $offset)) {
$lastError = preg_last_error();
foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new \RuntimeException('Matching failed with '.$k.'.');
}
}
throw new \RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}
return $ret ? $matches : null;
}
public function padBoth(int $length, string $padStr = ' '): self
{
$str = clone $this;
$str->string = str_pad($this->string, $length, $padStr, STR_PAD_BOTH);
return $str;
}
public function padEnd(int $length, string $padStr = ' '): self
{
$str = clone $this;
$str->string = str_pad($this->string, $length, $padStr, STR_PAD_RIGHT);
return $str;
}
public function padStart(int $length, string $padStr = ' '): self
{
$str = clone $this;
$str->string = str_pad($this->string, $length, $padStr, STR_PAD_LEFT);
return $str;
}
public function prepend(string ...$prefix): self
{
$str = clone $this;
$str->string = (1 >= \count($prefix) ? $prefix[0] ?? '' : implode('', $prefix)).$str->string;
return $str;
}
public function replace(string $from, string $to): self
{
$str = clone $this;
$str->string = $this->ignoreCase ? str_ireplace($from, $to, $this->string) : str_replace($from, $to, $this->string);
return $str;
}
public function replaceMatches(string $from, $to): self
{
if ('g' === $pattern[-1]) {
$pattern = substr($pattern, 0, -1);
}
if ($this->ignoreCase) {
$pattern .= 'i';
}
$replace = \is_array($to) || $to instanceof \Closure ? 'preg_replace_callback' : 'preg_replace';
set_error_handler(static function ($t, $m) { throw new \InvalidArgumentException($m); });
try {
if (null === $string = $replace($from, $to, $this->string)) {
$lastError = preg_last_error();
foreach (get_defined_constants(true)['pcre'] as $k => $v) {
if ($lastError === $v && '_ERROR' === substr($k, -6)) {
throw new \RuntimeException('Matching failed with '.$k.'.');
}
}
throw new \RuntimeException('Matching failed with unknown error code.');
}
} finally {
restore_error_handler();
}
$str = clone $this;
$str->string = $string;
return $str;
}
public function slice(int $start = 0, int $length = null): self
{
$str = clone $this;
$str->string = (string) substr($this->string, $start, $length ?? PHP_INT_MAX);
return $str;
}
public function snake(): self
{
$str = $this->toCamelCase();
$str->string = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $str->string));
return $str;
}
public function splice(string $replacement, int $start = 0, int $length = null): self
{
$str = clone $this;
$str = substr_replace($this->string, $replacement, $start, $length ?? PHP_INT_MAX);
return $str;
}
public function split(string $delimiter, int $limit = null): array
{
if ('' === $delimiter) {
return $this->chunk();
}
$chunks = $this->ignoreCase
? preg_split('{'.preg_quote($delimiter).'}i', $this->string, $limit ?? PHP_INT_MAX)
: explode($delimiter, $this->string, $limit ?? PHP_INT_MAX);
foreach ($chunks as $i => $chunk) {
$chunks[$i] = $str = clone $this;
$str->string = $chunk;
}
return $chunks;
}
public function startsWith(string $prefix): bool
{
if ('' === $prefix) {
return true;
}
return 0 === ($this->ignoreCase ? stripos($this->string, $prefix) : strpos($this->string, $prefix));
}
public function title(bool $allWords = false): self
{
$str = clone $this;
$str->string = $allWords ? ucwords($str->string) : ucfirst($str->string);
return $str;
}
public function trim(string $chars = " \t\n\r\0\x0B\x0C"): self
{
$str = clone $this;
$str->string = trim($str->string, $chars);
return $str;
}
public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C"): self
{
$str = clone $this;
$str->string = rtrim($str->string, $chars);
return $str;
}
public function trimStart(string $chars = " \t\n\r\0\x0B\x0C"): self
{
$str = clone $this;
$str->string = ltrim($str->string, $chars);
return $str;
}
public function upper(): self
{
$str = clone $this;
$str->string = strtoupper($str->string);
return $str;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment