Skip to content

Commit 13b2fed

Browse files
committed
Adds string object & related items.
1 parent 705f7bf commit 13b2fed

File tree

3 files changed

+313
-0
lines changed

3 files changed

+313
-0
lines changed

src/Helper/SingletonTrait.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace DevCoding\Helper;
4+
5+
/**
6+
* Trait to easily make a helper class into a singleton instance. Should be used sparingly.
7+
*
8+
* @package DevCoding\Helper
9+
*/
10+
trait SingletonTrait
11+
{
12+
/** @var static */
13+
protected static $instance;
14+
15+
/**
16+
* Returns an instance of the class, creating an instance if one is not already available.
17+
*
18+
* @return static
19+
*/
20+
public static function get()
21+
{
22+
if (empty(static::$instance))
23+
{
24+
static::$instance = new static();
25+
}
26+
27+
return static::$instance;
28+
}
29+
}

src/Helper/StringHelper.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace DevCoding\Helper;
4+
5+
/**
6+
* Helper that includes various string functions that do not belong in the StringLiteral object.
7+
*
8+
* @method static StringHelper get()
9+
*
10+
* @author AMJones <am@jonesiscoding.com>
11+
* @license https://github.com/deviscoding/objection/blob/main/LICENSE
12+
* @package DevCoding\Helper
13+
*/
14+
class StringHelper
15+
{
16+
use SingletonTrait;
17+
18+
/**
19+
* Evaluates whether the given value can be cast to a string without error.
20+
*
21+
* @param mixed $val the value to evaluate
22+
*
23+
* @return bool TRUE if the value may be safely cast to a string
24+
*/
25+
public function isStringable($val)
26+
{
27+
return is_scalar($val) || is_object($val) && method_exists($val, '__toString');
28+
}
29+
}

src/Object/Type/StringLiteral.php

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
<?php
2+
3+
namespace DevCoding\Object\Type;
4+
5+
use DevCoding\Helper\StringHelper;
6+
7+
/**
8+
* Object representing a string, which can be used as a string in most instances. Contains convenient methods for
9+
* modifying the string before output.
10+
*
11+
* @author AMJones <am@jonesiscoding.com>
12+
* @license https://github.com/deviscoding/objection/blob/main/LICENSE
13+
* @package DevCoding\Object\Type
14+
*/
15+
class StringLiteral
16+
{
17+
/** @var string */
18+
private $value;
19+
20+
public function __construct(string $value)
21+
{
22+
$this->value = $value;
23+
}
24+
25+
// region //////////////////////////////////////////////// Value Object Methods
26+
27+
/**
28+
* @return string
29+
*/
30+
public function __toString(): string
31+
{
32+
return $this->toNative();
33+
}
34+
35+
/**
36+
* Determines if the given value is the equivalent of the given string.
37+
*
38+
* @param mixed $value
39+
*/
40+
public function equals($value): bool
41+
{
42+
if ($value instanceof StringLiteral)
43+
{
44+
$string = $value->toNative();
45+
}
46+
elseif (StringHelper::get()->isStringable($value))
47+
{
48+
$string = (string) $value;
49+
}
50+
51+
return isset($string) && $string == $this->toNative();
52+
}
53+
54+
/**
55+
* Returns a StringLiteral object from the given value.
56+
*
57+
* @param string $val
58+
*/
59+
public static function fromNative($val): StringLiteral
60+
{
61+
return new static($val);
62+
}
63+
64+
/**
65+
* Returns a StringLiteral generated with "random" characters.
66+
*
67+
* @param int $length
68+
*
69+
* @throws \Exception if length is over 32 characters
70+
*/
71+
public static function fromRandom($length): StringLiteral
72+
{
73+
if ($length > 32)
74+
{
75+
throw new \Exception('Max length of random string is 32.');
76+
}
77+
78+
$string = substr(md5(rand(100000, 999999)), 0, $length);
79+
80+
return new static($string);
81+
}
82+
83+
/**
84+
* @return string
85+
*/
86+
public function toNative()
87+
{
88+
return $this->value;
89+
}
90+
91+
// endregion ///////////////////////////////////////////// End Value Object Methods
92+
93+
// region //////////////////////////////////////////////// Other Public Methods
94+
95+
/**
96+
* Converts this string into a CSS safe string that may be used for CSS class names or HTML5 IDs.
97+
*
98+
* @param string $sep The separator to use. Defaults to -
99+
* @param bool $spaces Allow spaces in the result. Defaults to FALSE
100+
* @param bool $force_lowercase Force the result to lowercase. Defaults to FALSE
101+
*
102+
* @return string|string[]|null
103+
*
104+
* @throws \Exception
105+
*/
106+
public function css($sep = '-', $spaces = false, $force_lowercase = false)
107+
{
108+
// Replace all accent characters with their non-accented equivalents.
109+
$string = $this->transliterate();
110+
111+
// Replace Spaces
112+
$add = ($spaces) ? ['\s', preg_quote($sep, '/')] : [null, preg_quote($sep, '/')];
113+
$regex = vsprintf('/([^a-zA-Z0-9_%s%s]+)/', $add);
114+
$string = preg_replace($regex, $sep, $string);
115+
116+
// Force Lowercase
117+
if ($force_lowercase)
118+
{
119+
$string = strtolower($string);
120+
}
121+
122+
// Make sure first character is NOT a number.
123+
if (is_numeric(substr($string, 0, 1)))
124+
{
125+
$digits = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
126+
$string = $digits[substr($string, 0, 1)].substr($string, 1);
127+
}
128+
129+
return $string;
130+
}
131+
132+
/**
133+
* Converts this string into the proper format for a PHP class name. IE- Converts 'table name' to 'TableName'.
134+
*
135+
* @param bool $transliterate whether to replace non-ASCII characters with non-accented equivalents where possible
136+
*
137+
* @return string
138+
*
139+
* @author Jonathan H. Wage <jonwage@gmail.com> (Borrowed from Doctrine Inflector)
140+
*/
141+
public function classify($transliterate = true)
142+
{
143+
$word = ($transliterate) ? $this->transliterate() : $this->toNative();
144+
145+
return str_replace([' ', '_', '-'], '', ucwords($word, ' _-'));
146+
}
147+
148+
/**
149+
* Converts this string into camelCase. IE- Converts 'table name' to 'tableName'.
150+
*
151+
* @param bool $transliterate
152+
*
153+
* @author Jonathan H. Wage <jonwage@gmail.com> (Borrowed from Doctrine Inflector)
154+
*
155+
* @return string
156+
*/
157+
public function camelize($transliterate = true)
158+
{
159+
return lcfirst($this->classify($transliterate));
160+
}
161+
162+
/**
163+
* Creates a "slug" from this string by optionally any non ASCII characters, then replacing all the spacing
164+
* characters with a separator, converting the string to lower case, and removing any non-alphanumeric characters.
165+
*
166+
* @param string $sep the separator to use, a dash by default
167+
* @param bool $transliterate Whether to replace non-ASCII accented characters with non-accented equivalents.
168+
* If this option is FALSE, all non-ASCII characters are simply removed.
169+
*
170+
* @return string
171+
*
172+
* @throws \Exception
173+
*/
174+
public function slugify($sep = '-', $transliterate = true)
175+
{
176+
$string = ($transliterate) ? $this->transliterate() : $this->toNative();
177+
178+
return strtolower(trim(preg_replace('~[^0-9a-z]+~i', $sep, $string)));
179+
}
180+
181+
/**
182+
* Removes and replaces accent characters, using the transliterator_transliterate function if possible.
183+
*
184+
* @return string the transliterated word or phrase
185+
*/
186+
public function transliterate()
187+
{
188+
try
189+
{
190+
if (function_exists('transliterator_transliterate'))
191+
{
192+
/* @noinspection PhpComposerExtensionStubsInspection */
193+
return transliterator_transliterate('Any-Latin; Latin-ASCII', $this->toNative());
194+
}
195+
}
196+
catch (\Exception $e)
197+
{
198+
// Proceed with the transliteration using the array of regex patterns and replacements below.
199+
}
200+
201+
$string = $this->toNative();
202+
$transliterations = [
203+
// Alphabetical
204+
'/À/' => 'A', '/Á/' => 'A', '/Â/' => 'A', '/Ã/' => 'A', '/Ä/' => 'Ae',
205+
'/Å/' => 'A', '/Ā/' => 'A', '/Ą/' => 'A', '/Ă/' => 'A', '/Æ/' => 'Ae',
206+
'/Ç/' => 'C', '/Ć/' => 'C', '/Č/' => 'C', '/Ĉ/' => 'C', '/Ċ/' => 'C',
207+
'/Ď/' => 'D', '/Đ/' => 'D', '/Ð/' => 'D', '/È/' => 'E', '/É/' => 'E',
208+
'/Ê/' => 'E', '/Ë/' => 'E', '/Ē/' => 'E', '/Ę/' => 'E', '/Ě/' => 'E',
209+
'/Ĕ/' => 'E', '/Ė/' => 'E', '/Ĝ/' => 'G', '/Ğ/' => 'G', '/Ġ/' => 'G',
210+
'/Ģ/' => 'G', '/Ĥ/' => 'H', '/Ħ/' => 'H', '/Ì/' => 'I', '/Í/' => 'I',
211+
'/Î/' => 'I', '/Ï/' => 'I', '/Ī/' => 'I', '/Ĩ/' => 'I', '/Ĭ/' => 'I',
212+
'/Į/' => 'I', '/İ/' => 'I', '/IJ/' => 'Ij', '/Ĵ/' => 'J', '/Ķ/' => 'K',
213+
'/Ł/' => 'L', '/Ľ/' => 'L', '/Ĺ/' => 'L', '/Ļ/' => 'L', '/Ŀ/' => 'L',
214+
'/Ñ/' => 'N', '/Ń/' => 'N', '/Ň/' => 'N', '/Ņ/' => 'N', '/Ŋ/' => 'N',
215+
'/Ò/' => 'O', '/Ó/' => 'O', '/Ô/' => 'O', '/Õ/' => 'O', '/Ö/' => 'Oe',
216+
'/Ø/' => 'O', '/Ō/' => 'O', '/Ő/' => 'O', '/Ŏ/' => 'O', '/Œ/' => 'Oe',
217+
'/Ŕ/' => 'R', '/Ř/' => 'R', '/Ŗ/' => 'R', '/Ś/' => 'S', '/Š/' => 'S',
218+
'/Ş/' => 'S', '/Ŝ/' => 'S', '/Ș/' => 'S', '/Ť/' => 'T', '/Ţ/' => 'T',
219+
'/Ŧ/' => 'T', '/Ț/' => 'T', '/Ù/' => 'U', '/Ú/' => 'U', '/Û/' => 'U',
220+
'/Ü/' => 'Ue', '/Ū/' => 'U', '/Ů/' => 'U', '/Ű/' => 'U', '/Ŭ/' => 'U',
221+
'/Ũ/' => 'U', '/Ų/' => 'U', '/Ŵ/' => 'W', '/Ý/' => 'Y', '/Ŷ/' => 'Y',
222+
'/Ÿ/' => 'Y', '/Y/' => 'Y', '/Ź/' => 'Z', '/Ž/' => 'Z', '/Ż/' => 'Z',
223+
'/Þ/' => 'T',
224+
'/à/' => 'a', '/á/' => 'a', '/â/' => 'a', '/ã/' => 'a', '/ä/' => 'ae',
225+
'/å/' => 'a', '/ā/' => 'a', '/ą/' => 'a', '/ă/' => 'a', '/æ/' => 'ae',
226+
'/ç/' => 'c', '/ć/' => 'c', '/č/' => 'c', '/ĉ/' => 'c', '/ċ/' => 'c',
227+
'/ď/' => 'd', '/đ/' => 'd', '/ð/' => 'd', '/è/' => 'e', '/é/' => 'e',
228+
'/ê/' => 'e', '/ë/' => 'e', '/ē/' => 'e', '/ę/' => 'e', '/ě/' => 'e',
229+
'/ĕ/' => 'e', '/ė/' => 'e', '/ĝ/' => 'g', '/ğ/' => 'g', '/ġ/' => 'g',
230+
'/ģ/' => 'g', '/ĥ/' => 'h', '/ħ/' => 'h', '/ì/' => 'i', '/í/' => 'i',
231+
'/î/' => 'i', '/ï/' => 'i', '/ī/' => 'i', '/ĩ/' => 'i', '/ĭ/' => 'i',
232+
'/į/' => 'i', '/ı/' => 'i', '/ij/' => 'ij', '/ĵ/' => 'j', '/ķ/' => 'k',
233+
'/ł/' => 'l', '/ľ/' => 'l', '/ĺ/' => 'l', '/ļ/' => 'l', '/ŀ/' => 'l',
234+
'/ñ/' => 'n', '/ń/' => 'n', '/ň/' => 'n', '/ņ/' => 'n', '/ŋ/' => 'n',
235+
'/ò/' => 'o', '/ó/' => 'o', '/ô/' => 'o', '/õ/' => 'o', '/ö/' => 'oe',
236+
'/ø/' => 'o', '/ō/' => 'o', '/ő/' => 'o', '/ŏ/' => 'o', '/œ/' => 'oe',
237+
'/ŕ/' => 'r', '/ř/' => 'r', '/ŗ/' => 'r', '/ś/' => 's', '/š/' => 's',
238+
'/ş/' => 's', '/ŝ/' => 's', '/ș/' => 's', '/ť/' => 't', '/ţ/' => 't',
239+
'/ŧ/' => 't', '/ț/' => 't', '/ù/' => 'u', '/ú/' => 'u', '/û/' => 'u',
240+
'/ü/' => 'ue', '/ū/' => 'u', '/ů/' => 'u', '/ű/' => 'u', '/ŭ/' => 'u',
241+
'/ũ/' => 'u', '/ų/' => 'u', '/ŵ/' => 'w', '/ý/' => 'y', '/ŷ/' => 'y',
242+
'/ÿ/' => 'y', '/y/' => 'y', '/ź/' => 'z', '/ž/' => 'z', '/ż/' => 'z',
243+
'/þ/' => 't', '/ß/' => 'ss', '/ſ/' => 'ss', '/ƒ/' => 'f', '/ĸ/' => 'k',
244+
'/ʼn/' => 'n', ];
245+
246+
foreach ($transliterations as $key => $value)
247+
{
248+
$string = preg_replace($key, $value, $string);
249+
}
250+
251+
return $string;
252+
}
253+
254+
// endregion ///////////////////////////////////////////// End Other Public Methods
255+
}

0 commit comments

Comments
 (0)