├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── StrHelper.php └── functions.php └── tests └── StrHelperTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | composer.lock 3 | vendor 4 | build -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | - 7.2 7 | 8 | env: 9 | matrix: 10 | - COMPOSER_FLAGS="--prefer-lowest" 11 | - COMPOSER_FLAGS="" 12 | 13 | before_script: 14 | - travis_retry composer self-update 15 | - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source 16 | 17 | script: phpunit --coverage-clover clover 18 | 19 | after_success: 20 | - wget https://scrutinizer-ci.com/ocular.phar 21 | - php ocular.phar code-coverage:upload --format=php-clover clover 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Abdulrahman M 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # str-helper 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/awssat/str-helper.svg?style=flat-square)](https://packagist.org/packages/awssat/str-helper) 4 | [![StyleCI](https://styleci.io/repos/111329905/shield?branch=master)](https://styleci.io/repos/111329905) 5 | [![Build Status](https://img.shields.io/travis/awssat/str-helper/master.svg?style=flat-square)](https://travis-ci.org/awssat/str-helper) 6 | 7 | 8 | ⚡️ A flexible, simple & yet powerful string manipulation helper for PHP. It gives you the magic of method chaining and it's easier and shorter to be included in views. It Supports most of [PHP built-in strings functions](http://php.net/manual/en/book.strings.php) (and other useful methods like: contains, equal, append, prepend ...). 9 | 10 | 11 | 12 | ```php 13 | str('Hi World')->replace(' ', '+')->lower(); 14 | ``` 15 | 16 | ## Why use this instead of other string maniplulation packages ? 17 | This is a wrapper for PHP default string functions, to provide a very poweful method chaining and conditions. 18 | You don't have to learn new methods names, just use PHP functions names that you know. 19 | 20 | 21 | ## Install/Use 22 | You can install the package via composer locally in your project folder: 23 | 24 | ```bash 25 | $ composer require awssat/str-helper 26 | ``` 27 | 28 | After installing it, just start using `StrHelper` class, or simply the helper function `str()`: 29 | 30 | 31 | ## Examples 32 | 33 | ```bash 34 | str('Hi Hello')->strReplace(' ', '-'); 35 | >> hi-hello 36 | ``` 37 | 38 | 39 | ```bash 40 | str('Hi Hello')->prepend('[')->append(']'); 41 | >> [Hi Hello] 42 | ``` 43 | 44 | In case you want an explicit string value for conditions, use "get": 45 | ```bash 46 | if(str('Hi')->lower->get() == 'hi') { 47 | echo 'yes'; 48 | } 49 | >> yes 50 | ``` 51 | 52 | 53 | There is a "tap" method: 54 | ```bash 55 | str('LINK.COM')->tap(function($v){ print($v); })->lower(); 56 | >> LINK.COM 57 | ``` 58 | 59 | for callbacks use "do" method: 60 | ```bash 61 | str('link.com')->do(function($string){ 62 | return strip_tags($string); 63 | }); 64 | >> link.com 65 | ``` 66 | or: 67 | ```bash 68 | str('link.com')->do(function(){ 69 | $this->stripTags(); 70 | }); 71 | >> link.com 72 | ``` 73 | 74 | you may notice using camelCase instead of snake_case for method name works too. 75 | 76 | 77 | You can also use conditions, if(..), else(), endif() 78 | ```php 79 | str('hi') 80 | ->ifContains('hi') 81 | ->upper(); 82 | >> HI 83 | ``` 84 | 85 | 86 | if can take an anonymous function 87 | ```php 88 | str('hi') 89 | ->if(function(){ 90 | return $this->contains('hi'); 91 | }) 92 | ->upper(); 93 | >> HI 94 | ``` 95 | 96 | All methods are availabe to be called statically, as: 97 | ```php 98 | StrHelper::capitalize('life'); 99 | ``` 100 | or using str() function. 101 | ```php 102 | str()::capitalize('nomad'); 103 | ``` 104 | 105 | 106 | __[UTF-8 Support] If mbstring library is installed and you call a strpos function, mb_strpos will be called instead.__ 107 | 108 | 109 | ## Tests 110 | Simply use: 111 | ```bash 112 | $ composer test 113 | ``` 114 | ## Credits 115 | - [Abdulrahman M.](https://github.com/abdumu) 116 | - [All Contributors](../../contributors) 117 | 118 | ## License 119 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 120 | 121 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awssat/str-helper", 3 | "description": "A flexible & powerful string manipulation helper for PHP", 4 | "keywords": [ 5 | "string", 6 | "laravel", 7 | "manipulation", 8 | "helper", 9 | "str" 10 | ], 11 | "homepage": "https://github.com/awssat/str-helper", 12 | "license": "MIT", 13 | "authors": [{ 14 | "name": "Awssat", 15 | "email": "hello@awssat.com" 16 | }], 17 | 18 | "require": { 19 | "php": "~7.0" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "^6.4" 23 | }, 24 | 25 | "autoload": { 26 | "psr-4": { 27 | "Awssat\\StrHelper\\": "src" 28 | }, 29 | "files": [ 30 | "src/functions.php" 31 | ] 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Awssat\\StrHelper\\Test\\": "tests" 36 | } 37 | }, 38 | "scripts": { 39 | "test": "vendor/bin/phpunit" 40 | }, 41 | "config": { 42 | "sort-packages": true 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/StrHelper.php: -------------------------------------------------------------------------------- 1 | 'strtolower', 103 | 'upper' => 'strtoupper', 104 | 'parse' => 'parse_str', 105 | 'length' => 'strlen', 106 | 'capitalize' => 'ucfirst', 107 | 'slice' => 'substr', 108 | 'index' => 'strpos', 109 | ]; 110 | 111 | /** 112 | * Initiate the class. 113 | * 114 | * @param string $value given string 115 | */ 116 | public function __construct($value = '') 117 | { 118 | $this->currentString = (string) $value; 119 | } 120 | 121 | public static function __callStatic($methodName, $arguments) 122 | { 123 | return (new static($arguments[0] ?? ''))->$methodName(array_slice($arguments, 1))->get(true); 124 | } 125 | 126 | /** 127 | * The door to the magic. 128 | * 129 | * @param string $methodName name of called method 130 | * @param array $arguments arguments of called method 131 | * 132 | * @return void 133 | */ 134 | public function __call($methodName, $arguments) 135 | { 136 | if (array_key_exists($methodName, $this->aliasMethods)) { 137 | $methodName = $this->aliasMethods[$methodName]; 138 | } 139 | 140 | // in case of if{method}() 141 | if (substr($methodName, 0, 2) === 'if') { 142 | $methodName = ltrim(substr($methodName, 2), '_'); 143 | 144 | return $this->if( 145 | function () use ($methodName, $arguments) { 146 | return $this->$methodName(...$arguments); 147 | } 148 | ); 149 | } 150 | 151 | //nothing to do, false condition was triggered 152 | if ($this->skipIfTriggered() && !$this->insideIfCondition) { 153 | return $this; 154 | } 155 | 156 | $snake_method_name = strtolower(preg_replace('/([a-z]{1})([A-Z]{1})/', '$1_$2', $methodName)); 157 | 158 | if (in_array($snake_method_name, ['parse_str', 'strlen', 'equal', 'contains', 'append', 'prepend'])) { 159 | return $this->do(function () use ($snake_method_name, $arguments) { 160 | if ($snake_method_name === 'parse_str') { 161 | $mb_parse_str = function_exists('mb_parse_str') ? 'mb_parse_str' : 'parse_str'; 162 | 163 | $mb_parse_str($this->currentString, $result); 164 | 165 | return $result; 166 | } elseif ($snake_method_name === 'strlen') { 167 | return $this->count(); 168 | } elseif ($snake_method_name === 'equal' && count($arguments)) { 169 | foreach ($arguments as $arg) { 170 | if ($this->currentString !== $arg) { 171 | return false; 172 | } 173 | } 174 | 175 | return true; 176 | } elseif ($snake_method_name === 'contains' && count($arguments)) { 177 | foreach ($arguments as $arg) { 178 | $mb_strpos = function_exists('mb_strpos') ? 'mb_strpos' : 'strpos'; 179 | 180 | if ($mb_strpos($this->currentString, $arg) === false) { 181 | return false; 182 | } 183 | } 184 | 185 | return true; 186 | } elseif ($snake_method_name === 'append') { 187 | return $this->currentString.implode('', $arguments); 188 | } elseif ($snake_method_name === 'prepend') { 189 | return implode('', $arguments).$this->currentString; 190 | } 191 | }); 192 | } elseif (function_exists($snake_method_name)) { 193 | if (function_exists('mb_'.$snake_method_name)) { 194 | $snake_method_name = 'mb_'.$snake_method_name; 195 | } 196 | 197 | // Regular functions -> do(methodName, ...) 198 | return $this->do($snake_method_name, ...$arguments); 199 | } else { 200 | // Couldn't find either? 201 | throw new \BadMethodCallException('Method ('.$methodName.') does not exist!'); 202 | } 203 | 204 | return $this; 205 | } 206 | 207 | /** 208 | * Get the processed string. 209 | * 210 | * @param bool $force ignore conditions 211 | * 212 | * @return string 213 | */ 214 | public function get($force = false) 215 | { 216 | if ($this->skipIfTriggered() && !$force) { 217 | return $this; 218 | } 219 | 220 | return $this->currentString; 221 | } 222 | 223 | /** 224 | * set current string. 225 | * 226 | * @return string 227 | */ 228 | public function set($value) 229 | { 230 | if ($this->skipIfTriggered()) { 231 | return $this; 232 | } 233 | 234 | $this->currentString = (string) $value; 235 | 236 | return $this; 237 | } 238 | 239 | /** 240 | * Tap! Tap! 241 | * 242 | * @param callable $callable Anonymous function 243 | * 244 | * @return self 245 | */ 246 | public function tap($callable) 247 | { 248 | $callable($this->currentString); 249 | 250 | return $this; 251 | } 252 | 253 | protected function skipIfTriggered($condition = null) 254 | { 255 | if ($this->conditionDepth === 0) { 256 | return false; 257 | } 258 | 259 | if ($condition === null) { 260 | $condition = $this->conditionDepth; 261 | if ($condition > 1 && $this->skipIfTriggered($condition - 1)) { 262 | return true; 263 | } 264 | } 265 | 266 | $isIf = $this->isIf[$condition] && $this->falseIfTriggered[$condition]; 267 | $isElse = $this->isElse[$condition] && $this->falseElseTriggered[$condition]; 268 | 269 | return $isIf || $isElse; 270 | } 271 | 272 | /** 273 | * Execute a callable on the string. 274 | * 275 | * @param callable $callable 276 | * @param mixed ...$args 277 | * 278 | * @return mixed 279 | */ 280 | public function do($callable, ...$args) 281 | { 282 | if ($this->skipIfTriggered() && !$this->insideIfCondition) { 283 | return $this; 284 | } 285 | 286 | if ($callable instanceof \Closure) { 287 | 288 | //anonymous 289 | $callable = $callable->bindTo($this); 290 | $result = ($callable->bindTo($this))($this->currentString); 291 | 292 | if (\is_object($result) && is_a($result, __CLASS__)) { 293 | $result = $result->get(); 294 | } elseif ($result === null) { 295 | return $this; 296 | } 297 | } elseif (function_exists($callable)) { 298 | //regular functions 299 | 300 | //If a regular function is called, then we get its information 301 | //and where is the position of the string value among the params 302 | $functionInfo = new \ReflectionFunction($callable); 303 | 304 | if ($functionInfo->getNumberOfParameters() > 1) { 305 | $stringIndex = 0; 306 | 307 | foreach ($functionInfo->getParameters() as $order => $arg) { 308 | if (in_array($arg->name, ['main_str', 'str1', 'str', 'string', 'subject', 'haystack', 'body', 'first'])) { 309 | $stringIndex = $order; 310 | break; 311 | } 312 | } 313 | 314 | //add the string param in the right order 315 | array_splice($args, $stringIndex, 0, [$this->currentString]); 316 | 317 | $result = $callable(...$args); 318 | } elseif ($functionInfo->getNumberOfParameters() == 1) { 319 | $result = $callable($this->currentString); 320 | } else { 321 | $result = $callable(); 322 | } 323 | } else { 324 | if (function_exists('str_'.$callable)) { 325 | return $this->do($callable, ...$args); 326 | } 327 | 328 | throw new \InvalidArgumentException('Method (do) can only receive anonymous functions or regular (built-in/global) functions!'); 329 | } 330 | 331 | if ( 332 | gettype($result) !== 'string' && 333 | !(isset($this->isIf[$this->conditionDepth]) && $this->isIf[$this->conditionDepth]) && 334 | !(isset($this->isElse[$this->conditionDepth]) && $this->isElse[$this->conditionDepth]) 335 | ) { 336 | if (is_array($result) && class_exists('\\Awssat\\ArrayHelper\\ArrayHelper')) { 337 | return new \Awssat\ArrayHelper\ArrayHelper($result); 338 | } 339 | 340 | return $result; 341 | } 342 | 343 | if ($this->insideIfCondition) { 344 | return $result; 345 | } 346 | 347 | $this->currentString = $result; 348 | 349 | return $this; 350 | } 351 | 352 | /** 353 | * If condition, to end the condition use endif(). 354 | * 355 | * @param callable $callable 356 | * @param mixed ...$args 357 | * 358 | * @return self 359 | */ 360 | public function if($callable, ...$args) 361 | { 362 | $this->conditionDepth++; 363 | 364 | $this->falseIfTriggered[$this->conditionDepth] = false; 365 | $this->falseElseTriggered[$this->conditionDepth] = false; 366 | $this->isIf[$this->conditionDepth] = true; 367 | $this->isElse[$this->conditionDepth] = false; 368 | 369 | $lastString = $this->currentString; 370 | 371 | $this->insideIfCondition = true; 372 | 373 | $result = $this->do($callable, ...$args); 374 | 375 | if ($result instanceof self && strcmp($lastString, $this->currentString) === 0) { 376 | $this->falseIfTriggered[$this->conditionDepth] = true; 377 | } elseif (gettype($result) && strcmp($lastString, $result) === 0) { 378 | $this->falseIfTriggered[$this->conditionDepth] = true; 379 | } elseif ($result instanceof \Traversable && count($result) == 0) { 380 | $this->falseIfTriggered[$this->conditionDepth] = true; 381 | } elseif ($result === false) { 382 | $this->falseIfTriggered[$this->conditionDepth] = true; 383 | } 384 | 385 | $this->insideIfCondition = false; 386 | 387 | return $this; 388 | } 389 | 390 | /** 391 | * End the If condition. 392 | * 393 | * @return self 394 | */ 395 | public function endif() 396 | { 397 | unset( 398 | $this->falseIfTriggered[$this->conditionDepth], 399 | $this->falseElseTriggered[$this->conditionDepth], 400 | $this->isIf[$this->conditionDepth], 401 | $this->isElse[$this->conditionDepth] 402 | ); 403 | 404 | $this->conditionDepth--; 405 | 406 | return $this; 407 | } 408 | 409 | /** 410 | * Else case of an initiated condition. 411 | * 412 | * @return self 413 | */ 414 | public function else() 415 | { 416 | $this->isIf[$this->conditionDepth] = false; 417 | $this->isElse[$this->conditionDepth] = true; 418 | 419 | if (!$this->falseIfTriggered[$this->conditionDepth]) { 420 | $this->falseElseTriggered[$this->conditionDepth] = true; 421 | } 422 | 423 | $this->falseIfTriggered[$this->conditionDepth] = false; 424 | 425 | return $this; 426 | } 427 | 428 | /** 429 | * Get the current string. 430 | * 431 | * @return string 432 | */ 433 | public function __toString() 434 | { 435 | return (string) $this->currentString; 436 | } 437 | 438 | /** 439 | * Return the length of the processed string. 440 | * 441 | * @return int 442 | */ 443 | public function count() 444 | { 445 | if (function_exists('mb_strlen')) { 446 | return mb_strlen($this->currentString); 447 | } 448 | 449 | return strlen($this->currentString); 450 | } 451 | } 452 | -------------------------------------------------------------------------------- /src/functions.php: -------------------------------------------------------------------------------- 1 | 12 | * 13 | * @return mixed|Awssat\StrHelper\StrHelper 14 | */ 15 | function str($value = null) 16 | { 17 | return new StrHelper($value); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/StrHelperTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('Awssat\StrHelper\StrHelper', get_class(str())); 14 | } 15 | 16 | /** @test */ 17 | public function str_can_take_numbers() 18 | { 19 | $this->assertEquals('1', str(1)); 20 | } 21 | 22 | /** @test */ 23 | public function str_can_take_booleans() 24 | { 25 | $this->assertEquals('', str(false)); 26 | } 27 | 28 | /** @test */ 29 | public function str_can_take_objects_that_can_be_string() 30 | { 31 | $this->assertEquals('x', str(new class() { 32 | public function __toString() 33 | { 34 | return 'x'; 35 | } 36 | })->get()); 37 | } 38 | 39 | /** @test */ 40 | public function empty_str_object_type_is_valid() 41 | { 42 | $this->assertEquals('Awssat\StrHelper\StrHelper', get_class(str())); 43 | } 44 | 45 | /** @test */ 46 | public function does_str_has_methods() 47 | { 48 | $this->assertTrue( 49 | method_exists(get_class(str()), 'count'), 50 | 'str()->count() does not exist!' 51 | ); 52 | } 53 | 54 | /** @test */ 55 | public function str_will_cast_to_string() 56 | { 57 | $this->assertEquals('HI', str('hi')->upper()); 58 | } 59 | 60 | /** @test */ 61 | public function str_check_methods_return_boolean() 62 | { 63 | $this->assertTrue(str('hi')->upper()->equal('HI')); 64 | } 65 | 66 | /** @test */ 67 | public function str_methods_type_is_an_object() 68 | { 69 | $this->assertEquals('object', gettype(str('hi')->upper())); 70 | } 71 | 72 | /** @test */ 73 | public function str_get_method_type_is_a_string() 74 | { 75 | $this->assertEquals('string', gettype(str('hi')->upper()->get())); 76 | } 77 | 78 | /** @test */ 79 | public function tap_works_fine() 80 | { 81 | $this->assertEquals( 82 | 'HI', 83 | str('hi')->tap(function ($value) { 84 | $value = 'welcome'; 85 | })->upper() 86 | ); 87 | } 88 | 89 | /** @test */ 90 | public function do_can_bring_magic() 91 | { 92 | $this->assertEquals( 93 | 'hi', 94 | str('hi')->do(function ($string) { 95 | return strip_tags($string); 96 | }) 97 | ); 98 | } 99 | 100 | /** @test */ 101 | public function do_can_bring_magic_once() 102 | { 103 | $this->assertEquals( 104 | 'hi2', 105 | str('hi2')->do(function () { 106 | $this->stripTags(); 107 | }) 108 | ); 109 | } 110 | 111 | /** @test */ 112 | public function do_can_bring_magic_twice() 113 | { 114 | $this->assertEquals( 115 | 'hi', 116 | str('hi')->do('strip_tags') 117 | ); 118 | } 119 | 120 | /** @test */ 121 | public function do_can_bring_magic_even_better() 122 | { 123 | $this->assertEquals( 124 | 'boo', 125 | str('boo')->stripTags() 126 | ); 127 | } 128 | 129 | /** @test */ 130 | public function test_if_with_built_in_functions() 131 | { 132 | $result = str('hi') 133 | ->ifStrpos('hi') 134 | ->upper() 135 | ->endif(); 136 | 137 | $this->assertEquals('HI', $result); 138 | } 139 | 140 | /** @test */ 141 | public function test_if2_with_built_in_functions() 142 | { 143 | $result = str('howdy') 144 | ->ifStrReplace('hi', 'welcome') 145 | ->upper(); 146 | 147 | $this->assertEquals('howdy', $result); 148 | } 149 | 150 | /** @test */ 151 | public function test_if_endif_with_built_in_functions() 152 | { 153 | $result = str('HOWDY') 154 | ->ifStrReplace('hi', 'WELCOME') 155 | ->upper() 156 | ->endif() 157 | ->stripTags() 158 | ->lower(); 159 | 160 | $this->assertEquals('howdy', $result); 161 | } 162 | 163 | /** @test */ 164 | public function test_if_else_endif_with_built_in_functions() 165 | { 166 | $result = []; 167 | 168 | foreach (['hi', 'WELCOME'] as $word) { 169 | $result[] = str($word) 170 | ->ifContains('hi') 171 | ->upper() 172 | ->append(' you.') 173 | ->else() 174 | ->lower() 175 | ->append(' aboard.') 176 | ->endif() 177 | ->prepend('User, '); 178 | } 179 | 180 | $this->assertEquals('User, HI you. User, welcome aboard.', implode(' ', $result)); 181 | } 182 | 183 | /** @test */ 184 | public function test_built_in_functions() 185 | { 186 | foreach ([ 187 | 'strpos' => [ 188 | 'wife', //str 189 | ['i'], //params 190 | '1', //expectted 191 | ], 192 | 'strReplace' => [ 193 | 'once', 194 | ['c', 'z'], 195 | 'onze', 196 | ], 197 | 'str_replace' => [ 198 | 'twice', 199 | ['c', 'x'], 200 | 'twixe', 201 | ], 202 | 'strrchr' => [ 203 | 'life is an illusion', 204 | ['a'], 205 | 'an illusion', 206 | ], 207 | 'explode' => [ 208 | 'a b c d', 209 | [' '], 210 | ['a', 'b', 'c', 'd'], 211 | ], 212 | ] as $func => $data) { 213 | $this->assertEquals($data[2], str($data[0])->{$func}(...$data[1])); 214 | } 215 | } 216 | 217 | /** @test */ 218 | public function str_throw_exception_if_given_wrong_method() 219 | { 220 | $this->expectException(\BadMethodCallException::class); 221 | str('If it is complicated, then you are doing it wrong!')->callMama(); 222 | } 223 | 224 | /** @test */ 225 | public function str_do_throw_exception_if_given_no_function() 226 | { 227 | $this->expectException(\InvalidArgumentException::class); 228 | str('Go Duck Yourself')->do('hi'); 229 | } 230 | 231 | /** @test */ 232 | public function str_can_be_counted() 233 | { 234 | $this->assertEquals(5, count(str('nomad')->capitalize())); 235 | } 236 | 237 | /** @test */ 238 | public function str_methods_can_be_called_statically() 239 | { 240 | $this->assertEquals('Nomad', str()::capitalize('nomad')); 241 | 242 | $this->assertEquals('Life', StrHelper::capitalize('life')); 243 | } 244 | } 245 | --------------------------------------------------------------------------------