Completed
Push — master ( acb468...819269 )
by Ben
02:38
created

HasMagicAttributes::isCollection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
namespace Thinktomorrow\MagicAttributes;
4
5
trait HasMagicAttributes
6
{
7
    public function attr($key, $default = null, $closure = null)
8
    {
9
        if (method_exists($this, $key)) {
10
            return $this->{$key}();
11
        }
12
13
        /**
14
         * We first try to fetch the key as is, and then we try to fetch
15
         * with converting camelcase to dot syntax as well.
16
         */
17
        $value = null;
18
19
        foreach([$key, $this->camelCaseToDotSyntax($key)] as $k) {
20
            if(null !== ($value = $this->retrieveAttributeValue($k))) {
21
                break;
22
            }
23
        }
24
25
        if (is_null($value)) {
26
            return $default;
27
        }
28
29
        return is_callable($closure)
30
            ? call_user_func_array($closure, [$value, $this])
31
            : $value;
32
    }
33
34
    private function retrieveAttributeValue($key)
35
    {
36
        $keys = explode('.', $key);
37
        $parent = $this;
38
        $value = null;
39
40
        foreach ($keys as $k) {
41
            $value = $this->retrieveValue($k, $parent);
42
43
            if (is_null($value)) {
44
                return null;
45
            }
46
47
            $parent = $value;
48
        }
49
50
        return $value;
51
    }
52
53
    private function retrieveValue($key, $parent)
54
    {
55
        if(null !== ($value = $this->retrieveProperty($key, $parent))) return $value;
56
57
        /**
58
         * At this point, we know that the key isn't present as property.
59
         * We now check if its an array consisting itself of nested items
60
         * so we can try to pluck the values by key from those arrays / objects.
61
         */
62
        if( ! $this->isMultiDimensional($parent)) return null;
63
64
        return $this->pluck($key, $parent);
65
    }
66
67
    private function isMultiDimensional($array): bool
68
    {
69
        // A eloquent collection is always considered multidimensional
70
        if($this->isCollection($array)) return true;
71
72
        if( !is_array($array)) return false;
73
74
        if(count($array) != count($array, COUNT_RECURSIVE )) return true;
75
76
        // If count is the same, it still could be a list of objects
77
        // which we will treat the same as a multidim. array
78
        return is_object(reset($array));
79
    }
80
81
    private function pluck($key, $list)
82
    {
83
        if( ! $this->isAccessibleAsArray($list)) return null;
84
85
        $values = [];
86
87
        foreach($list as $item) {
88
            if($value = $this->retrieveProperty($key, $item)) {
89
                $values[] = $value;
90
            }
91
        }
92
93
        return count($values) > 0 ? $values : null;
94
    }
95
96
    private function retrieveProperty($key, $parent)
97
    {
98
        if (is_object($parent) && isset($parent->$key)) {
99
            return $parent->$key;
100
        }
101
102
        if ($this->isAccessibleAsArray($parent) && isset($parent[$key])) {
103
            return $parent[$key];
104
        }
105
106
        return null;
107
    }
108
109
    /**
110
     * @param $key
111
     * @return string
112
     */
113
    private function camelCaseToDotSyntax($key): string
114
    {
115
        return strtolower(preg_replace('/(?<!^)[A-Z]/', '.$0', $key));
116
    }
117
118
    private function isAccessibleAsArray($value)
119
    {
120
        return is_array($value) || $this->isCollection($value);
121
    }
122
123
    /**
124
     * Check if value is a Collection with ArrayAccess.
125
     *
126
     * @param $value
127
     * @return bool
128
     */
129
    private function isCollection($value)
130
    {
131
        return (is_object($value) &&  $value instanceof \ArrayAccess);
132
    }
133
}
134