Passed
Push — develop ( e8138b...fd59c9 )
by Glynn
01:29 queued 27s
created

createWith()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 38
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 22
nc 2
nop 2
dl 0
loc 38
rs 9.2568
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Object functions.
5
 *
6
 * This file is part of PinkCrab Function Constructors.
7
 *
8
 * PinkCrab Function Constructors is free software: you can redistribute it and/or modify it under the terms of the
9
 * GNU General Public License as published by the Free Software Foundation, either version 2
10
 * of the License, or (at your option) any later version.
11
 *
12
 * PinkCrab Function Constructors is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
13
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 * See the GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with PinkCrab Function Constructors.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 *
19
 * @author Glynn Quelch <[email protected]>
20
 * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
21
 * @package PinkCrab\FunctionConstructors
22
 * @since 0.0.1
23
 *
24
 * @template Number of int|float
25
 * @phpstan-template Number of int|float
26
 * @psalm-template Number of int|float
27
 * @template Class of object|class-string
28
 * @phpstan-template Class of object|class-string
29
 * @psalm-template Class of object|class-string
30
 */
31
32
declare(strict_types=1);
33
34
namespace PinkCrab\FunctionConstructors\Objects;
35
36
use Closure;
37
use InvalidArgumentException;
38
use PinkCrab\FunctionConstructors\Comparisons as C;
0 ignored issues
show
Bug introduced by
The type PinkCrab\FunctionConstructors\Comparisons was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
39
40
/**
41
 * Returns a function for checking if the passed class or string-class-name matches
42
 * that of the predefined class or string-class-name.
43
 *
44
 * @template Class of object|class-string
45
 * @param Class $class
0 ignored issues
show
Bug introduced by
The type PinkCrab\FunctionConstructors\Objects\Class was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
46
 * @return Closure(Class): bool
47
 */
48
function isInstanceOf($class): Closure
49
{
50
51
    // If we have an object, get full name.
52
    if (is_object($class)) {
53
        $class = get_class($class);
54
    }
55
    if (! class_exists($class)) {
56
        throw new InvalidArgumentException(__FUNCTION__ . 'only accepts a Class or Class Name');
57
    }
58
59
    /**
60
     * @param Class $value
61
     * @return bool
62
     */
63
    return function ($value) use ($class): bool {
64
        if (is_object($value)) {
65
            $value = get_class($value);
66
        }
67
        if (! class_exists($value)) {
68
            throw new InvalidArgumentException(__FUNCTION__ . 'only accepts a Class or Class Name');
69
        }
70
        return is_a($value, $class, true);
71
    };
72
}
73
74
/**
75
 * Returns a function to check if a passed class or string-class-name implements a predefined interface.
76
 *
77
 * @template Class of object|class-string
78
 * @param Class $interface The interface to check against.
79
 * @return Closure(Class): bool
80
 */
81
function implementsInterface($interface): Closure
82
{
83
    /**
84
     * @param Class $class
85
     * @return bool
86
     */
87
    return function ($class) use ($interface): bool {
88
        return in_array(
89
            $interface,
90
            class_implements($class, false) ?: array(),
91
            true
92
        );
93
    };
94
}
95
96
/**
97
 * Returns a function for turning objects into arrays.
98
 * Only takes public properties.
99
 *
100
 * @return Closure(object):array<string, mixed>
101
 */
102
function toArray(): Closure
103
{
104
    /**
105
     * @param object $object
106
     * @return array<string, mixed>
107
     */
108
    return function ($object): array {
109
110
        // If not object, return empty array.
111
        if (! is_object($object)) {
112
            return array();
113
        }
114
115
        $objectVars = get_object_vars($object);
116
        return array_reduce(
117
            array_keys($objectVars),
118
            function (array $array, $key) use ($objectVars): array {
119
                $array[ ltrim((string) $key, '_') ] = $objectVars[ $key ];
120
                return $array;
121
            },
122
            array()
123
        );
124
    };
125
}
126
127
128
/**
129
 * Returns a closure for checking if an object uses a trait.
130
 *
131
 * @param string $trait
132
 * @param bool $autoload Whether to call __autoload by default.
133
 * @return Closure(object):bool
134
 */
135
function usesTrait(string $trait, bool $autoload = true): Closure
136
{
137
    /**
138
     * @param object|class-string $object
139
     * @return bool
140
     */
141
    return function ($object) use ($trait, $autoload): bool {
142
        // Throw type error if not object or class-string.
143
        if (! is_object($object) && ! class_exists($object, false)) {
144
            throw new InvalidArgumentException(__FUNCTION__ . ' only accepts an object or class-string');
145
        }
146
147
        $traits = array();
148
        do {
149
            $traits = array_merge(class_uses($object, $autoload) ?: array(), $traits);
150
        } while ($object = get_parent_class($object));
151
152
        return in_array($trait, $traits, true);
153
    };
154
};
155
156
/**
157
 * Creates a class which allows the creation of an object with a set of predefined properties and pass additionals.
158
 *
159
 * @template Class of object
160
 * @param class-string<Class> $class
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string<Class> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Class>.
Loading history...
161
 * @param array<string, mixed> $baseProperties
162
 * @return Closure(array<string, mixed>):Class
163
 */
164
function createWith(string $class, array $baseProperties = array()): Closure
165
{
166
    // Get all expected constrcutor args as an array of keys with any default values.
167
    $constructor = new \ReflectionClass($class);
168
    $constructorArgs = $constructor->getConstructor() ? $constructor->getConstructor()->getParameters() : array();
169
    $constructorArgs = array_reduce(
170
        $constructorArgs,
171
        function (array $args, \ReflectionParameter $param) use ($baseProperties): array {
172
            // If the param exists in base properties use that, else use the default value or null.
173
            $args[ $param->getName() ] = \array_key_exists($param->getName(), $baseProperties)
174
                ? $baseProperties[ $param->getName() ]
175
                : ($param->isDefaultValueAvailable()
176
                    ? $param->getDefaultValue()
177
                    : null);
178
            return $args;
179
        },
180
        array()
181
    );
182
183
    /**
184
     * @param array<string, mixed> $properties
185
     * @return Class
186
     */
187
    return function (array $properties = array()) use ($class, $constructorArgs) {
188
        // Loop through constructorArgs and replace with any passed properties.
189
        $constructorArgs = array_reduce(
190
            array_keys($constructorArgs),
191
            function (array $args, string $key) use ($constructorArgs, $properties): array {
192
                $args[] = \array_key_exists($key, $properties)
193
                    ? $properties[ $key ]
194
                    : $constructorArgs[ $key ];
195
                return $args;
196
            },
197
            array()
198
        );
199
200
201
        return new $class(...$constructorArgs);
202
    };
203
}
204