Passed
Push — master ( cb950d...14a06b )
by Nelson
03:22
created

Text::compare()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 6
nop 2
dl 0
loc 21
ccs 13
cts 13
cp 1
crap 6
rs 9.2222
c 0
b 0
f 0
1
<?php
2
/**
3
 * PHP: Nelson Martell Library file
4
 *
5
 * Copyright © 2015-2019 Nelson Martell (http://nelson6e65.github.io)
6
 *
7
 * Licensed under The MIT License (MIT)
8
 * For full copyright and license information, please see the LICENSE
9
 * Redistributions of files must retain the above copyright notice.
10
 *
11
 * @copyright 2015-2019 Nelson Martell
12
 * @link      http://nelson6e65.github.io/php_nml/
13
 * @since     0.7.0
14
 * @license   http://www.opensource.org/licenses/mit-license.php The MIT License (MIT)
15
 * */
16
namespace NelsonMartell\Extensions;
17
18
use InvalidArgumentException;
19
20
use Cake\Utility\Text as TextBase;
21
22
use NelsonMartell\IComparer;
23
use NelsonMartell\StrictObject;
24
25
use function NelsonMartell\msg;
26
use function NelsonMartell\typeof;
27
28
/**
29
 * Provides extension methods to handle strings.
30
 * This class is based on \Cake\Utility\Text of CakePHP(tm) class.
31
 *
32
 * @since 0.7.0
33
 * @author Nelson Martell <[email protected]>
34
 * @see \Cake\Utility\Text::insert()
35
 * @link http://book.cakephp.org/3.0/en/core-libraries/text.html
36
 * */
37
class Text extends TextBase implements IComparer
38
{
39
40
    /**
41
     * Replaces format elements in a string with the string representation of an
42
     * object matching the list of arguments specified. You can give as many
43
     * params as you need, or an array with values.
44
     *
45
     * ##Usage
46
     * Using numbers as placeholders (encloses between `{` and `}`), you can get
47
     * the matching string representation of each object given. Use `{0}` for
48
     * the fist object, `{1}` for the second, and so on:
49
     *
50
     * ```php
51
     * $format = '{0} is {1} years old, and have {2} cats.';
52
     * echo Text::format($format, 'Bob', 65, 101); // 'Bob is 65 years old, and have 101 cats.'
53
     * ```
54
     *
55
     * You can also use an array to give objects values:
56
     *
57
     * ```php
58
     * $format = '{0} is {1} years old, and have {2} cats.';
59
     * $data   = ['Bob', 65, 101];
60
     * echo Text::format($format, $data); // 'Bob is 65 years old, and have 101 cats.'
61
     * ```
62
     *
63
     * This is specially useful to be able to use non-numeric placeholders (named placeholders):
64
     *
65
     * ```php
66
     * $format = '{name} is {age} years old, and have {n} cats.';
67
     * $data = ['name' => 'Bob', 'n' => 101, 'age' => 65];
68
     * echo Text::format($format, $data); // 'Bob is 65 years old, and have 101 cats.'
69
     * ```
70
     *
71
     * For numeric placeholders, yo can convert the array into a list of arguments.
72
     *
73
     * ```php
74
     * $format = '{0} is {1} years old, and have {2} cats.';
75
     * $data   = ['Bob', 65, 101];
76
     * echo Text::format($format, ...$data); // 'Bob is 65 years old, and have 101 cats.'
77
     * ```
78
     *
79
     * > Note: If objects are not convertible to string, it will throws and catchable exception
80
     * (`InvalidArgumentException`).
81
     *
82
     * @param string      $format An string containing variable placeholders to be replaced. If you provide name
83
     *   placeholders, you must pass the target array as
84
     * @param array|mixed $args   Object(s) to be replaced into $format placeholders.
85
     *   You can provide one item only of type array for named placeholders replacement. For numeric placeholders, you
86
     *   can still pass the array or convert it into arguments by using the '...' syntax instead.
87
     *
88
     * @return string
89
     * @throws InvalidArgumentException if $format is not an string or placeholder values are not string-convertibles.
90
     * @todo   Implement formatting, like IFormatProvider or something like that.
91
     * @author Nelson Martell <[email protected]>
92
     */
93 236
    public static function format($format, ...$args)
94
    {
95 236
        static $options = [
96
            'before'  => '{',
97
            'after'   => '}',
98
        ];
99
100 236
        $originalData = $args;
101
102
        // Make it compatible with named placeholders along numeric ones if passed only 1 array as argument
103 236
        if (count($args) === 1 && is_array($args[0])) {
104 225
            $originalData = $args[0];
105
        }
106
107 236
        $data = [];
108
        // Sanitize values to be convertibles into strings
109 236
        foreach ($originalData as $placeholder => $value) {
110 236
            $valueType = typeof($value);
111
112 236
            if ($valueType->canBeString() === false) {
113 3
                $msg = 'Value for "{{0}}" placeholder is not convertible to string; "{1}" type given.';
114 3
                throw new InvalidArgumentException(msg($msg, $placeholder, $valueType));
115
            }
116
117
            // This is to work-arround a bug in use of ``asort()`` function in ``Text::insert`` (at v3.2.5)
118
            // without SORT_STRING flag... by forcing value to be string.
119 236
            settype($value, 'string');
120 236
            $data[$placeholder] = $value;
121
        }
122
123 236
        return static::insert($format, $data, $options);
124
    }
125
126
    /**
127
     * Ensures that object given is not null. If is `null`, throws and exception.
128
     *
129
     * @param mixed $obj Object to validate
130
     *
131
     * @return mixed Same object
132
     * @throws InvalidArgumentException if object is `null`.
133
     */
134 146
    public static function ensureIsNotNull($obj)
135
    {
136 146
        if (is_null($obj)) {
137
            $msg = msg('Provided object must not be NULL.');
138
            throw new InvalidArgumentException($msg);
139
        }
140
141 146
        return $obj;
142
    }
143
144
    /**
145
     * Ensures that object given is an string. Else, thows an exception
146
     *
147
     * @param mixed $obj Object to validate.
148
     *
149
     * @return string Same object given, but ensured that is an string.
150
     * @throws InvalidArgumentException if object is not an `string`.
151
     */
152 146
    public static function ensureIsString($obj)
153
    {
154 146
        if (!is_string(static::ensureIsNotNull($obj))) {
155
            $msg = msg('Provided object must to be an string; "{0}" given.', typeof($obj));
156
            throw new InvalidArgumentException($msg);
157
        }
158
159 146
        return $obj;
160
    }
161
162
    /**
163
     * Ensures that given string is not empty.
164
     *
165
     * @param string $string String to validate.
166
     *
167
     * @return string Same string given, but ensured that is not empty.
168
     * @throws InvalidArgumentException if string is null or empty.
169
     */
170
    public static function ensureIsNotEmpty($string)
171
    {
172
        if (static::ensureIsString($string) === '') {
173
            $msg = msg('Provided string must not be empty.');
174
            throw new InvalidArgumentException($msg);
175
        }
176
177
        return $string;
178
    }
179
180
    /**
181
     * Ensures that given string is not empty or whitespaces.
182
     *
183
     * @param string $string String to validate.
184
     *
185
     * @return string Same string given, but ensured that is not whitespaces.
186
     * @throws InvalidArgumentException if object is not an `string`.
187
     * @see    \trim()
188
     */
189
    public static function ensureIsNotWhiteSpaces($string)
190
    {
191
        if (trim(static::ensureIsNotEmpty($string)) === '') {
192
            $msg = msg('Provided string must not be white spaces.');
193
            throw new InvalidArgumentException($msg);
194
        }
195
196
        return $string;
197
    }
198
199
    /**
200
     * Ensures that an string follows the PHP variables naming convention.
201
     *
202
     * @param string $string String to be ensured.
203
     *
204
     * @return string
205
     * @throws InvalidArgumentException if object is not an `string` or do not
206
     *   follows the PHP variables naming convention.
207
     *
208
     * @see PropertyExtension::ensureIsValidName()
209
     */
210 144
    public static function ensureIsValidVarName($string)
211
    {
212 144
        $pattern = '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/';
213
214 144
        if (!preg_match($pattern, static::ensureIsString($string))) {
215
            $msg = msg('Provided string do not follows PHP variables naming convention: "{0}".', $string);
216
            throw new InvalidArgumentException($msg);
217
        }
218
219 144
        return $string;
220
    }
221
222
223
    /**
224
     * {@inheritDoc}
225
     *
226
     * This methods is specific for the case when one of them are `string`. In other case, will fallback to
227
     * `StrictObject::compare()`.` You should use it directly instead of this method as comparation function
228
     * for `usort()`.
229
     *
230
     * @param string|mixed $left
231
     * @param string|mixed $right
232
     *
233
     * @return int|null
234
     *
235
     * @since 1.0.0
236
     * @see StrictObject::compare()
237
     */
238 23
    public static function compare($left, $right)
239
    {
240 23
        if (is_string($left)) {
241 23
            if (typeof($right)->isCustom()) { // String are minor than classes
242 5
                return -1;
243 19
            } elseif (typeof($right)->canBeString()) {
244 17
                return strnatcmp($left, $right);
245
            } else {
246 2
                return -1;
247
            }
248 6
        } elseif (is_string($right)) {
249 6
            $r = static::compare($right, $left);
250
251 6
            if ($r !== null) {
0 ignored issues
show
introduced by
The condition $r !== null is always true.
Loading history...
252 6
                $r *= -1; // Invert result
253
            }
254
255 6
            return $r;
256
        }
257
258 1
        return StrictObject::compare($left, $right);
259
    }
260
}
261