• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

piko-framework / core / 23950426174

03 Apr 2026 02:08PM UTC coverage: 97.802% (-2.2%) from 100.0%
23950426174

push

github

ilhooq
Bind also magick properties

0 of 2 new or added lines in 1 file covered. (0.0%)

89 of 91 relevant lines covered (97.8%)

2.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.24
/src/ModelTrait.php
1
<?php
2

3
/**
4
 * This file is part of Piko - Web micro framework
5
 *
6
 * @copyright 2019-2022 Sylvain PHILIP
7
 * @license LGPL-3.0; see LICENSE.txt
8
 * @link https://github.com/piko-framework/core
9
 */
10

11
declare(strict_types=1);
12

13
namespace Piko;
14

15
use ReflectionClass;
16
use ReflectionProperty;
17

18
/**
19
 * Base model trait.
20
 *
21
 * @author Sylvain PHILIP <contact@sphilip.com>
22
 */
23
trait ModelTrait
24
{
25
    /**
26
     * Errors hash container
27
     *
28
     * @var array<string>
29
     */
30
    protected $errors = [];
31

32
    /**
33
     * Get the public properties reprenting the data model
34
     *
35
     * @return array<mixed>
36
     */
37
    protected function getAttributes(): array
38
    {
39
        $class = get_called_class();
5✔
40
        $reflection = new ReflectionClass($class);
5✔
41
        $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC);
5✔
42
        $attributes = [];
5✔
43

44
        foreach ($properties as $property) {
5✔
45
            /* @var $property \ReflectionProperty */
46
            if ($property->class === $class) {
5✔
47
                $attributes[$property->name] = $this->{$property->name} ?? null;
5✔
48
            }
49
        }
50

51
        return $attributes;
5✔
52
    }
53

54
    /**
55
     * Bind the data to the model attributes.
56
     *
57
     * @param array<mixed> $data An array of data (name-value pairs).
58
     * @return void
59
     */
60
    public function bind(array $data): void
61
    {
62
        $reflection = new ReflectionClass($this);
6✔
63

64
        foreach ($data as $key => $value) {
6✔
65

66
            if ($reflection->hasProperty($key)) {
6✔
67
                $property = $reflection->getProperty($key);
6✔
68

69
                if ($property->isPublic() && $property->class === $reflection->getName()) {
6✔
70
                    $this->{$key} = $this->castValueForProperty($property, $value);
6✔
71
                }
NEW
72
            } elseif (isset($this->{$key})) { // in case of __isset magick method
×
NEW
73
                $this->{$key} = $value;
×
74
            }
75
        }
76
    }
77

78
    /**
79
     * Cast a bound value according to the declared property type.
80
     *
81
     * @param ReflectionProperty $property
82
     * @param mixed $value
83
     *
84
     * @return mixed
85
     */
86
    private function castValueForProperty(ReflectionProperty $property, $value)
87
    {
88
        if ($value === null) {
6✔
89
            return null;
1✔
90
        }
91

92
        $type = $property->getType();
6✔
93

94
        if ($type === null) {
6✔
95
            return $value;
1✔
96
        }
97

98
        $typeName = $type->getName();
5✔
99

100
        if ($type->allowsNull() && $value === '' && $typeName !== 'string') {
5✔
101
            return null;
1✔
102
        }
103

104
        return match ($typeName) {
4✔
105
            'int' => (int) $value,
1✔
106
            'float' => (float) $value,
1✔
107
            'bool' => $this->castBooleanValue($value),
2✔
108
            'string' =>  (string) $value,
1✔
109
            default => $value
4✔
110
        };
4✔
111
    }
112

113
    /**
114
     * Cast a value to a boolean using common form representations.
115
     *
116
     * @param mixed $value
117
     *
118
     * @return bool
119
     */
120
    private function castBooleanValue($value): bool
121
    {
122
        if (is_bool($value)) {
2✔
123
            return $value;
1✔
124
        }
125

126
        $filtered = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
2✔
127

128
        if ($filtered !== null) {
2✔
129
            return $filtered;
1✔
130
        }
131

132
        return (bool) $value;
1✔
133
    }
134

135
    /**
136
     * Get the model data as an associative array.
137
     *
138
     * @return array<mixed>
139
     */
140
    public function toArray(): array
141
    {
142
        return $this->getAttributes();
5✔
143
    }
144

145
    /**
146
     * Return the errors hash container
147
     *
148
     * @return array<string>
149
     */
150
    public function getErrors(): array
151
    {
152
        return $this->errors;
1✔
153
    }
154

155
    /**
156
     * Set an error that will be appended to the errors container
157
     *
158
     * @param string $errorName
159
     * @param string $errorMsg
160
     *
161
     * @see ModelTrait::$errors
162
     */
163
    protected function setError(string $errorName, string $errorMsg): void
164
    {
165
        $this->errors[$errorName] = $errorMsg;
1✔
166
    }
167

168
    /**
169
     * Validate this model (Should be extended).
170
     * Inherited method should fill the errors array using the setError method if the model is not valid.
171
     *
172
     * @see ModelTrait::setError()
173
     * @see ModelTrait::isValid()
174
     *
175
     * @codeCoverageIgnore
176
     *
177
     * @return void
178
     */
179
    protected function validate(): void
180
    {
181
    }
182

183
    /**
184
     * Check if the model is valid
185
     *
186
     * @return boolean
187
     */
188
    public function isValid(): bool
189
    {
190
        $this->validate();
1✔
191

192
        return empty($this->errors);
1✔
193
    }
194
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc