Passed
Push — master ( 980804...08b834 )
by Mihail
09:48
created

functions.php (3 issues)

1
<?php
2
3
/*
4
 * This file is part of the Koded package.
5
 *
6
 * (c) Mihail Binev <[email protected]>
7
 *
8
 * Please view the LICENSE distributed with this source code
9
 * for the full copyright and license information.
10
 *
11
 */
12
13
namespace Koded\Stdlib;
14
15
use DateTimeImmutable;
16
use FilesystemIterator;
17
use Koded\Stdlib\Interfaces\{Argument, Data};
18
use Koded\Stdlib\Serializer\XmlSerializer;
19
use RecursiveDirectoryIterator;
20
use RecursiveIteratorIterator;
21
22
/**
23
 * Creates a new Argument instance
24
 * with optional arbitrary number of arguments.
25
 *
26
 * @param array ...$values
27
 *
28
 * @return Argument
29
 */
30
function arguments(...$values): Argument
31
{
32 1
    return new Arguments(...$values);
33
}
34
35
/**
36
 * Creates a new Immutable instance
37
 * with optional arbitrary number of arguments.
38
 *
39
 * @param array ...$values
40
 *
41
 * @return Data
42
 */
43
function value(...$values): Data
44
{
45 1
    return new Immutable(...$values);
46
}
47
48
/**
49
 * HTML encodes a string.
50
 * Useful for escaping the input values in HTML templates.
51
 *
52
 * @param string $input    The input string
53
 * @param string $encoding The encoding
54
 *
55
 * @return string
56
 */
57
function htmlencode(string $input, string $encoding = 'UTF-8'): string
58
{
59 1
    return htmlentities($input, ENT_QUOTES | ENT_HTML5, $encoding);
60
}
61
62
/**
63
 * Creates a random generated string with optional prefix and/or suffix.
64
 *
65
 * NOTE: DO NOT use it for passwords or any data that requires cryptographic secureness!
66
 *
67
 * @param int    $length [optional]
68
 * @param string $prefix [optional]
69
 * @param string $suffix [optional]
70
 *
71
 * @return string
72
 * @throws \Exception if it was not possible to gather sufficient entropy
73
 * @since 1.10.0
74
 */
75
function randomstring(int $length = 16, string $prefix = '', string $suffix = ''): string
76
{
77 5
    $buffer = '';
78 5
    for ($x = 0; $x < $length; ++$x) {
79 5
        $buffer .= '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[random_int(0, 61)];
80
    }
81
82 5
    return $prefix . $buffer . $suffix;
83
}
84
85
/**
86
 * Transforms the simple snake_case string into CamelCaseName.
87
 *
88
 * @param string $string
89
 *
90
 * @return string Camel-cased string
91
 */
92
function snake_to_camel_case(string $string): string
93
{
94 10
    $string = preg_replace('/[\W\_]++/', ' ', $string);
95 10
    return str_replace(' ', '', ucwords($string));
96
}
97
98
/**
99
 * Transforms simple CamelCaseName into camel_case_name (lower case underscored).
100
 *
101
 * @param string $string CamelCase string to be underscored
102
 *
103
 * @return string Transformed string (for weird strings, you get what you deserve)
104
 */
105
function camel_to_snake_case(string $string): string
106
{
107 5
    $string = snake_to_camel_case($string);
108 5
    return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', trim($string)));
109
}
110
111
/**
112
 * Converts the string with desired delimiter character.
113
 *
114
 * @param string $string
115
 * @param int    $delimiter chr() of the delimiter character
116
 *
117
 * @return string The converted string with the provided delimiter
118
 */
119
function to_delimited_string(string $string, int $delimiter): string
120
{
121 3
    $str = preg_split('~[^\p{L}\p{N}\']+~u', trim($string));
122 3
    return join(chr($delimiter), $str);
0 ignored issues
show
It seems like $str can also be of type false; however, parameter $pieces of join() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

122
    return join(chr($delimiter), /** @scrutinizer ignore-type */ $str);
Loading history...
123
}
124
125
/**
126
 * Converts the string to-kebab-case
127
 *
128
 * @param string $string
129
 *
130
 * @return string
131
 */
132
function to_kebab_string(string $string): string
133
{
134 1
    return strtolower(to_delimited_string($string, ord('-')));
135
}
136
137
/**
138
 * Returns the JSON representation of a value.
139
 *
140
 * @param mixed $value   The data to be serialized
141
 * @param int   $options [optional] JSON bitmask options for JSON encoding
142
 *
143
 * @return string JSON encoded string, or EMPTY STRING if encoding failed
144
 * @see http://php.net/manual/en/function.json-encode.php
145
 */
146
function json_serialize($value, int $options = JSON_PRESERVE_ZERO_FRACTION | JSON_UNESCAPED_SLASHES): string
147
{
148 10
    if (false === $json = json_encode($value, $options)) {
149 1
        error_log(__FUNCTION__, json_last_error_msg(), $value);
150 1
        return '';
151
    }
152
153 9
    return $json;
154
}
155
156
/**
157
 * Decodes a JSON string into appropriate PHP type.
158
 *
159
 * @param string $json A JSON string
160
 *
161
 * @return mixed The decoded value, or EMPTY STRING on error
162
 */
163
function json_unserialize(string $json)
164
{
165 5
    $data = json_decode($json, false, 512, JSON_BIGINT_AS_STRING);
166
167 5
    if (JSON_ERROR_NONE !== json_last_error()) {
168 2
        error_log(__FUNCTION__, json_last_error_msg(), $json);
169 2
        return '';
170
    }
171
172 3
    return $data;
173
}
174
175
/**
176
 * Serializes the data into XML document.
177
 *
178
 * @param string   $root The XML document root name
179
 * @param iterable $data The data to be encoded
180
 *
181
 * @return string XML document
182
 */
183
function xml_serialize(string $root, iterable $data): string
184
{
185
    return (new XmlSerializer($root))->serialize($data);
186
}
187
188
/**
189
 * Unserialize an XML document into PHP array.
190
 * This function does not deal with magical conversions
191
 * of complicated XML structures.
192
 *
193
 * @param string $root The XML document root name
194
 * @param string $xml  The XML document to be decoded into array
195
 *
196
 * @return array Decoded version of the XML string,
197
 *               or empty array on malformed XML
198
 */
199
function xml_unserialize(string $root, string $xml): array
200
{
201 1
    return (new XmlSerializer($root))->unserialize($xml);
202
}
203
204
/**
205
 * Send a formatted error message to PHP's system logger.
206
 *
207
 * @param string $function The function name where error occurred
208
 * @param string $message  The error message
209
 * @param mixed  $data     Original data passed into function
210
 */
211
function error_log(string $function, string $message, $data): void
212
{
213 3
    \error_log(sprintf('(%s) [Error] - %s - data: %s', $function, $message, var_export($data, true)));
214 3
}
215
216
/**
217
 * Checks if the array is an associative array.
218
 *
219
 * Simple rules:
220
 *
221
 * - If all keys are sequential starting from 0..n, it is not an associative array
222
 * - empty array is not associative
223
 *
224
 * Unfortunately, the internal typecast to integer on the keys makes
225
 * the sane programming an ugly PHP Array Oriented Programming hackery.
226
 *
227
 * @param array $array
228
 *
229
 * @return bool
230
 */
231
function is_associative(array $array): bool
232
{
233 25
    return (bool)array_diff_assoc($array, array_values($array));
234
}
235
236
/**
237
 * Gets an instance of DateTimeImmutable in UTC.
238
 *
239
 * @return DateTimeImmutable
240
 */
241
function now(): DateTimeImmutable
242
{
243 1
    return date_create_immutable('now', timezone_open('UTC'));
0 ignored issues
show
Bug Best Practice introduced by
The expression return date_create_immut..., timezone_open('UTC')) could return the type false which is incompatible with the type-hinted return DateTimeImmutable. Consider adding an additional type-check to rule them out.
Loading history...
It seems like timezone_open('UTC') can also be of type false; however, parameter $timezone of date_create_immutable() does only seem to accept DateTimeZone|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

243
    return date_create_immutable('now', /** @scrutinizer ignore-type */ timezone_open('UTC'));
Loading history...
244
}
245
246
/**
247
 * Removes a directory.
248
 *
249
 * @param string $dirname The folder name
250
 *
251
 * @return bool TRUE on success, FALSE otherwise
252
 */
253
function rmdir(string $dirname): bool
254
{
255 1
    $deleted = [];
256
257
    /** @var \SplFileInfo $path */
258 1
    foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dirname, FilesystemIterator::SKIP_DOTS),
259 1
        RecursiveIteratorIterator::CHILD_FIRST) as $path) {
260 1
        $deleted[] = ($path->isDir() && false === $path->isLink()) ? \rmdir($path->getPathname()) : \unlink($path->getPathname());
261
    }
262
263 1
    return (bool)array_product($deleted);
264
}
265