Test Failed
Push — features/47-laravelmethods ( eb62da...02a3bd )
by Luke
02:43
created

functions.php ➔ _()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 2
dl 0
loc 10
ccs 5
cts 5
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/*
3
 * Nozavroni/Collections
4
 * Just another collections library for PHP5.6+.
5
 *
6
 * @copyright Copyright (c) 2016 Luke Visinoni <[email protected]>
7
 * @author    Luke Visinoni <[email protected]>
8
 * @license   https://github.com/nozavroni/collections/blob/master/LICENSE The MIT License (MIT)
9
 */
10
namespace Noz;
11
12
use Closure;
13
use Illuminate\Support\Str;
14
use InvalidArgumentException;
15
use Iterator;
16
use Noz\Collection\Collection;
17
use Noz\Collection\Sequence;
18
use Noz\Contracts\CollectionInterface;
19
use RuntimeException;
20
use Traversable;
21
22
/**
23
 * Collection factory.
24
 *
25
 * Simply an alias to (new Collection($in)). Allows for a little more concise and
26
 * simpler instantiation of a collection. Also I plan to eventually support
27
 * additional input types that will make this function more flexible and forgiving
28
 * than simply instantiating a Collection object, but for now the two are identical.
29
 *
30
 * @param array|Iterator $data Either an array or an iterator of data
31
 *
32
 * @return CollectionInterface
0 ignored issues
show
Documentation introduced by
Should the return type not be Illuminate\Support\Collection?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
33
 */
34
function collect($data = null)
35
{
36 44
    return Collection::factory($data);
37
}
38
39
/**
40
 * Invoke a callable and return result.
41
 *
42
 * Pass in a callable followed by whatever arguments you want passed to
43
 * it and this function will invoke it with your arguments and return
44
 * the result.
45
 *
46
 * @param callable $callback The callback function to invoke
47
 * @param array ...$args     The args to pass to your callable
48
 *
49
 * @return mixed The result of your invoked callable
50
 */
51
function invoke(callable $callback, ...$args)
52
{
53 7
    return call_user_func($callback, ...$args);
54
}
55
56
/**
57
 * Underscore function.
58
59
 * This function is meant to work sort of like jQuery's "$()". It is a contextual catch-all type function. It works
60
 * as a short-hand alias for invoke, collect, and with.
61
62
 * @param callable|mixed    $in
63
 * @param mixed ...         $_
64
 *
65
 * @return mixed|CollectionInterface
66
 */
67
function _($in, ...$args)
68
{
69 5
    if (is_callable($in)) {
70 3
        return invoke($in, ...$args);
71
    }
72 2
    if (is_traversable($in)) {
73 1
        return collect($in);
74
    }
75 1
    return $in;
76
}
77
78
/**
79
 * Determine if data is traversable.
80
 *
81
 * Pass in any variable and this function will tell you whether or not it
82
 * is traversable. Basically this just means that it is either an array or an iterator.
83
 * This function was written simply because I was tired of if statements that checked
84
 * whether a variable was an array or a descendant of \Iterator. So I wrote this guy.
85
 *
86
 * @param mixed $data The variable to determine traversability
87
 *
88
 * @return bool True if $input is an array or an Iterator
89
 */
90
function is_traversable($data)
91
{
92 98
    return is_array($data) || $data instanceof Traversable;
93
}
94
95
/**
96
 * Can data be converted to an array?
97
 *
98
 * @param mixed $data The data to check
99
 *
100
 * @return bool
101
 */
102
function is_arrayable($data)
103
{
104 71
    if (!is_array($data)) {
105 48
        if (is_object($data)) {
106
            return (
107 5
                method_exists($data, 'toArray') ||
108
                $data instanceof Traversable
109 5
            );
110
        }
111 46
        return false;
112
    }
113 40
    return true;
114
}
115
116
/**
117
 * Convert any traversable to an array.
118
 *
119
 * @todo I'm not sure if this function is necessary or not. Does iterator_to_array do everything this can do?
120
 *
121
 * @param Traversable $data Traversable data
122
 *
123
 * @return array
124
 */
125
function traversable_to_array(Traversable $data)
126
{
127 3
    $arr = [];
128 3
    foreach ($data as $key => $val) {
129 3
        $arr[$key] = $val;
130 3
    }
131 3
    return $arr;
132
}
133
134
/**
135
 * Convert data to an array.
136
 *
137
 * Accepts any kind of data and converts it to an array. If strict mode is on, only data that returns true from
138
 * is_arrayable() will be converted to an array. Anything else will cause an InvalidArgumentException to be thrown.
139
140
 * @param mixed $data   Data to convert to array
141
 * @param bool  $strict Whether to use strict mode
142
143
 * @return array
144
 *
145
 * @throws InvalidArgumentException
146
 */
147
function to_array($data, $strict = true)
148
{
149 41
    if (is_arrayable($data)) {
150 40
        if (is_array($data)) {
151 39
            return $data;
152
        }
153
        // this is what makes toArray() work recursively
154
        // it must stay right where it is do not move it
155 3
        if (method_exists($data, 'toArray')) {
156 3
            return $data->toArray();
157
        }
158 2
        if ($data instanceof Traversable) {
159 2
            return traversable_to_array($data);
160
        }
161
    }
162 2
    if ($strict) {
163 1
        throw new InvalidArgumentException(sprintf(
164 1
            'Invalid argument for "%s". Cannot convert "%s" to an array.',
165 1
            __FUNCTION__,
166 1
            typeof($data)
167 1
        ));
168
    }
169 1
    if (is_object($data)) {
170 1
        $values = [];
171 1
        foreach ($data as $key => $val) {
172 1
            $values[$key] = $val;
173 1
        }
174 1
        return $values;
175
    }
176 1
    if (is_null($data)) {
177 1
        return [];
178
    }
179 1
    return [$data];
180
}
181
182
/**
183
 * Get object count.
184
 *
185
 * This function will accept any data and attempt to return its count.
186
 *
187
 * @param mixed $data The data to count
188
 *
189
 * @return int
190
 */
191
function get_count($data)
192
{
193 11
    if (is_null($data)) {
194 4
        return $data;
195
    }
196
197 9
    if (is_array($data)) {
198 1
        return count($data);
199
    }
200
201 9
    if (is_numeric($data)) {
202 8
        return (int) $data;
203
    }
204
205 4
    if ($data === '') {
206 1
        return 0;
207
    }
208
209 4
    if ($data === 0) {
210
        return 0;
211
    }
212
213 4
    if (is_object($data)) {
214 3
        if (method_exists($data, 'count')) {
215 1
            $count = $data->count();
216 3
        } elseif (method_exists($data, '__toString')) {
217 3
            $count = (string) $data;
218 3
            $count = (int) $count;
219 3
        } elseif (is_traversable($data)) {
220
            $count = 0;
221
            foreach ($data as $item) {
222
                $count++;
223
            }
224
            return (int) $count;
225
        }
226 3
        if (isset($count)) {
227 3
            return (int) $count;
228
        }
229
    }
230
231 1
    throw new RuntimeException('Cannot convert to int.');
232
}
233
234
/**
235
 * Normalize offset to positive integer.
236
 *
237
 * Provided with the requested offset, whether it be a string, an integer (positive or negative), or some type of
238
 * object, this function will normalize it to a positive integer offset or, failing that, it will throw an exception.
239
 * A negative offset will require either the traversable that is being indexed or its total count in order to normalize
240
241
 * @param int|mixed $offset The offset to normalize
242
 * @param int|array|traversable $count  Either the traversable count, or the traversable itself.
243
244
 * @return int
245
246
 * @throws RuntimeException If offset cannot be normalized
247
 * @throws InvalidArgumentException If offset is negative and count is not provided
248
 */
249
function normalize_offset($offset, $count = null)
250
{
251 10
    if (is_object($offset) && method_exists($offset, '__toString')) {
252 1
        $offset = (string) $offset;
253 1
    }
254
255 10
    if (!is_numeric($offset)) {
256 2
        throw new RuntimeException('Invalid offset.');
257
    }
258
259 8
    $count = get_count($count);
260
261 8
    if ($offset < 0) {
262 6
        if (is_null($count)) {
263 1
            throw new InvalidArgumentException('Cannot normalize negative offset without a total count.');
264
        }
265 5
        $offset += $count;
266 5
    }
267 7
    return (int) $offset;
268
}
269
270
/**
271
 * Get range start and end from string.
272
 *
273
 * Provided a string in the format of "start:end" and the total items in a collection, this function will return an
274
 * array in the form of [start, length].
275
 *
276
 * @param string $range The range to get (in the format of "start:end"
277
 * @param int|array|traversable $count  Either the traversable count, or the traversable itself.
278
 *
279
 * @return array [start, length]
280
 */
281
function get_range_start_end($range, $count = null)
282
{
283 5
    $count = get_count($count);
284 5
    if (Str::contains($range, Sequence::SLICE_DELIM)) {
285
        // return slice as a new sequence
286 4
        list($start, $end) = explode(Sequence::SLICE_DELIM, $range, 2);
287 4
        if ($start == '') {
288 4
            $start = 0;
289 4
        }
290 4
        if ($end == '') {
291 4
            $end = $count - 1;
292 4
        }
293 4
        $start = normalize_offset($start, $count);
294 4
        $end = normalize_offset($end, $count) + 1;
295 4
        if ($end > $start) {
296 4
            $length = $end - $start;
297 4
            return [$start, $length];
298
        }
299
    }
300 1
    throw new RuntimeException('Invalid index range/offset.');
301
}
302
303
/**
304
 * Get data type.
305
 *
306
 * Inspects data to determine its type.
307
 *
308
 * @param mixed  $data       The data to check
309
 * @param bool   $meta       Whether to include meta data such as length/size
310
// * @param string $returnType What type of value to return (array or string)
0 ignored issues
show
Bug introduced by
There is no parameter named $returnType. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
311
 *
312
 * @return string
313
 */
314
function typeof($data, $meta = true/*, $returnType = 'string'*/)
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
315
{
316 4
    $type = gettype($data);
317 4
    if ($meta) {
318
        switch($type) {
319 4
            case 'object':
320 2
                $class = get_class($data);
321 2
                return "{$type} <{$class}>";
322 3
            case 'resource':
323 1
                $restype = get_resource_type($data);
324 1
                return "{$type} <{$restype}>";
325
        }
326 3
    } else {
327
        switch($type) {
328 1
            case 'object':
329 1
                return get_class($data);
330 1
            case 'resource':
331 1
                return get_resource_type($data);
332
        }
333
    }
334 3
    return $type;
335
}
336
337
// BEGIN debug/testing functions
338
339
/**
340
 * Dump and die.
341
 *
342
 * @param mixed $input Data to dump
343
 * @param bool  $exit  Should we exit after dump?
344
 * @param bool  $label Should we print a label?
345
 * @codeCoverageIgnore
346
 */
347
function dd($input, $exit = true, $label = null)
348
{
349
    if (is_null($label)) {
350
        $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
351
        $label = 'File: ';
352
        $label .= pathinfo($trace[0]['file'], PATHINFO_FILENAME);
353
        $label .= ':' . $trace[0]['line'];
354
        echo $label . "\n";
355
    } else {
356
        echo $label . "\n" . implode(
357
                array_map(
358
                    function () {
359
                        return '-';
360
                    },
361
                    str_split($label)
362
                )
363
            ) . "\n";
364
    }
365
    var_dump($input);
366
    echo "\n";
367
    if ($exit) {
368
        exit;
369
    }
370
}
371
372
/**
373
 * Exactly the same as var_dump, except that it returns its output rather than dumping it.
374
 */
375
function sdump($var)
376
{
377 27
    ob_start();
378 27
    var_dump($var);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($var); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
379 27
    return ob_get_clean();
380
}
381
382
/**
383
 * Get object hash/checksum.
384
 * Using a var_dump of an object, this will return a hash that tells you if anything in your object has changed. Just
385
 * create a hash of an object, do some stuff with it, then create another hash of it and compare the two. If they are
386
 * different, teh object has changed in some way.
387
 *
388
 * @param object $obj The object to hash
389
 * @param string $alg The hash algorithm (supports md5 or sha1)
390
 *
391
 * @return string
392
 *
393
 * @throws InvalidArgumentException
394
 */
395
function object_hash($obj, $alg = 'md5')
396
{
397 27
    $algorithms = ['md5', 'sha1'];
398 27
    if (!in_array($alg, $algorithms)) {
399 1
        throw new InvalidArgumentException(sprintf(
400 1
            '"%s" is not a valid hash algorithm (%s).',
401 1
            $alg,
402 1
            implode(', ', $algorithms)
403 1
        ));
404
    }
405 26
    return call_user_func($alg, sdump($obj));
406
}
407