GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Prototyped   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 49
dl 0
loc 194
rs 10
c 2
b 0
f 0
wmc 9

13 Methods

Rating   Name   Duplication   Size   Complexity  
B from() 0 30 8
assign() 0 11 ?
A get_class_reflection() 0 6 1
A hp$0 ➔ __sleep() 0 7 1
to_array() 0 10 ?
A hp$0 ➔ get_object_vars() 0 16 2
A hp$0 ➔ assign() 0 11 3
to_json() 0 7 ?
A hp$0 ➔ to_json() 0 7 1
A assignable() 0 3 1
A hp$0 ➔ to_array() 0 10 2
__sleep() 0 7 ?
get_object_vars() 0 16 ?
1
<?php
2
3
/*
4
 * This file is part of the ICanBoogie package.
5
 *
6
 * (c) Olivier Laviale <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ICanBoogie;
13
14
use AllowDynamicProperties;
0 ignored issues
show
Bug introduced by
The type AllowDynamicProperties 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...
15
use Closure;
16
use ICanBoogie\Accessor\AccessorReflection;
17
use ICanBoogie\Accessor\SerializableTrait;
18
use ICanBoogie\Prototype\UnableToInstantiate;
19
use ReflectionClass;
20
use ReflectionException;
21
use Throwable;
22
23
use function array_fill_keys;
24
use function array_intersect_key;
25
use function array_keys;
26
use function get_called_class;
27
use function get_object_vars;
28
use function is_callable;
29
use function is_string;
30
use function json_encode;
31
32
/**
33
 * Together with the {@link Prototype} class the {@link Prototyped} class provides means to
34
 * define getters and setters, as well as define getters, setters, and method at runtime.
35
 *
36
 * The class also provides a method to create instances in the same fashion PDO creates instances
37
 * with the `FETCH_CLASS` mode, that is the properties of the instance are set *before* its
38
 * constructor is invoked.
39
 */
40
#[AllowDynamicProperties]
41
class Prototyped implements ToArrayRecursive
42
{
43
    use ToArrayRecursiveTrait;
44
    use PrototypeTrait;
45
    use SerializableTrait;
0 ignored issues
show
Bug introduced by
The trait ICanBoogie\Accessor\SerializableTrait requires the property $class which is not provided by ICanBoogie\Prototyped.
Loading history...
46
47
    public const ASSIGN_SAFE = false;
48
    public const ASSIGN_UNSAFE = true;
49
50
    /**
51
     * Creates a new instance of the class using the supplied properties.
52
     *
53
     * The method tries to create the instance in the same fashion as
54
     * [PDO](http://www.php.net/manual/en/book.pdo.php) with the `FETCH_CLASS` mode, that is the
55
     * properties of the instance are set *before* its constructor is invoked.
56
     *
57
     * @param array<string, mixed> $properties Properties to be set before the constructor is invoked.
58
     * @param mixed[] $construct_args Arguments passed to the constructor.
59
     * @param class-string|null $class_name The name of the instance class. If empty, the name of the
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|null.
Loading history...
60
     * called class is used.
61
     *
62
     * @return object The new instance.
63
     *
64
     * @throws UnableToInstantiate
65
     */
66
    public static function from(array $properties = [], array $construct_args = [], string $class_name = null): object
67
    {
68
        if (!$class_name) {
69
            $class_name = get_called_class();
70
        }
71
72
        try {
73
            $class_reflection = self::get_class_reflection($class_name);
74
75
            if (!$properties) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $properties of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
76
                return $class_reflection->newInstanceArgs($construct_args);
77
            }
78
79
            $instance = $class_reflection->newInstanceWithoutConstructor();
80
81
            if ($instance instanceof self) {
82
                $instance->assign($properties, self::ASSIGN_UNSAFE);
83
            } else {
84
                foreach ($properties as $property => $value) {
85
                    $instance->$property = $value;
86
                }
87
            }
88
89
            if ($class_reflection->hasMethod('__construct') && is_callable([ $instance, '__construct' ])) {
90
                $instance->__construct(...$construct_args);
91
            }
92
93
            return $instance;
94
        } catch (Throwable $e) {
95
            throw new UnableToInstantiate("Unable to instantiate `$class_name`.", 0, $e);
96
        }
97
    }
98
99
    /**
100
     * Returns assignable properties.
101
     *
102
     * @return string[]
103
     */
104
    public static function assignable(): array
105
    {
106
        return [];
107
    }
108
109
    /**
110
     * @var array<class-string, ReflectionClass<object>>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string, ReflectionClass<object>> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string, ReflectionClass<object>>.
Loading history...
111
     */
112
    private static $class_reflection_cache = [];
113
114
    /**
115
     * Returns cached class reflection.
116
     *
117
     * @template T of object
118
     *
119
     * @param class-string<T> $class_name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
120
     *
121
     * @throws ReflectionException
122
     *
123
     * @return ReflectionClass<T>
124
     */
125
    private static function get_class_reflection(string $class_name): ReflectionClass
126
    {
127
        $reflection = &self::$class_reflection_cache[$class_name];
128
129
        /** @phpstan-ignore-next-line */
130
        return $reflection ?? $reflection = new ReflectionClass($class_name);
131
    }
132
133
    /**
134
     * Returns the public properties of an instance.
135
     *
136
     * @return array<string, mixed>
137
     */
138
    private static function get_object_vars(object $object): array
139
    {
140
        static $get_object_vars;
141
142
        if (!$get_object_vars) {
143
            $get_object_vars = Closure::bind(function (object $object) {
144
                return get_object_vars($object);
145
            },
146
                null,
147
                get_class(
148
                    new class {
149
                    }
150
                )); // Because `stdClass` is a no-no in PHP7
151
        }
152
153
        return $get_object_vars($object);
154
    }
155
156
    /**
157
     * The method returns an array of key/key pairs.
158
     *
159
     * Properties for which a lazy getter is defined are discarded. For instance, if the property
160
     * `next` is defined and the class of the instance defines the getter `lazy_get_next()`, the
161
     * property is discarded.
162
     *
163
     * Note that façade properties are also included.
164
     *
165
     * Warning: The code used to export private properties seams to produce frameless exception on
166
     * session close. If you encounter this problem you might want to override the method. Don't
167
     * forget to remove the prototype property!
168
     *
169
     * @return array<string, mixed>
170
     *
171
     * @throws ReflectionException
172
     */
173
    public function __sleep()
174
    {
175
        $keys = $this->accessor_sleep();
176
177
        unset($keys['prototype']);
178
179
        return $keys;
180
    }
181
182
    /**
183
     * Assigns properties to an object.
184
     *
185
     * @param array<string, mixed> $properties The properties to assign.
186
     * @param bool $unsafe The properties are not filtered if `true`.
187
     *
188
     * @return $this
0 ignored issues
show
Documentation Bug introduced by
The doc comment $this at position 0 could not be parsed: '$this' is only available from within classes.
Loading history...
189
     */
190
    public function assign(array $properties, bool $unsafe = self::ASSIGN_SAFE): object
191
    {
192
        if (!$unsafe) {
193
            $properties = array_intersect_key($properties, array_fill_keys(static::assignable(), null));
194
        }
195
196
        foreach ($properties as $property => $value) {
197
            $this->$property = $value;
198
        }
199
200
        return $this;
201
    }
202
203
    /**
204
     * Converts the object into an array.
205
     *
206
     * Only public properties and façade properties are included.
207
     *
208
     * @return array<string, mixed>
209
     *
210
     * @throws ReflectionException
211
     */
212
    public function to_array(): array
213
    {
214
        $array = self::get_object_vars($this);
215
216
        /** @phpstan-ignore-next-line */
217
        foreach (array_keys(AccessorReflection::resolve_facade_properties($this)) as $name) {
218
            $array[$name] = $this->$name;
219
        }
220
221
        return $array;
222
    }
223
224
    /**
225
     * Converts the object into a JSON string.
226
     */
227
    public function to_json(): string
228
    {
229
        $json = json_encode($this->to_array_recursive());
230
231
        assert(is_string($json));
232
233
        return $json;
234
    }
235
}
236