Completed
Push — master ( b31e17...a9e5d9 )
by Jared
02:30
created

Errors::getModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/**
4
 * @author Jared King <[email protected]>
5
 *
6
 * @link http://jaredtking.com
7
 *
8
 * @copyright 2015 Jared King
9
 * @license MIT
10
 */
11
namespace Pulsar;
12
13
use ArrayAccess;
14
use ArrayIterator;
15
use Countable;
16
use IteratorAggregate;
17
use Infuse\Locale;
18
19
class Errors implements IteratorAggregate, Countable, ArrayAccess
20
{
21
    /**
22
     * @var array
23
     */
24
    private $stack = [];
25
26
    /**
27
     * @var Model
28
     */
29
    private $model;
30
31
    /**
32
     * @var Locale
33
     */
34
    private $locale;
35
36
    /**
37
     * @var int
38
     */
39
    private $pointer = 0;
0 ignored issues
show
Unused Code introduced by
The property $pointer is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
40
41
    /**
42
     * @param Model|string $model class name of model
43
     *
44
     * @var Locale
45
     */
46
    public function __construct($model = null, Locale $locale = null)
47
    {
48
        $this->model = $model;
0 ignored issues
show
Documentation Bug introduced by
It seems like $model can also be of type string. However, the property $model is declared as type object<Pulsar\Model>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
49
        $this->locale = $locale;
50
    }
51
52
    /**
53
     * Gets the model instance for these errors.
54
     *
55
     * @return Model|string|null
56
     */
57
    public function getModel()
58
    {
59
        return $this->model;
60
    }
61
62
    /**
63
     * Gets the locale instance.
64
     *
65
     * @return Locale
66
     */
67
    public function getLocale()
68
    {
69
        return $this->locale;
70
    }
71
72
    /**
73
     * Adds an error message to the stack.
74
     *
75
     * @param string $property name of property the error is about
76
     * @param string $error    error code or message
77
     *
78
     * @return self
79
     */
80
    public function add($property, $error)
81
    {
82
        if (!isset($this->stack[$property])) {
83
            $this->stack[$property] = [];
84
        }
85
86
        $this->stack[$property][] = $error;
87
88
        return $this;
89
    }
90
91
    /**
92
     * Gets all of the errors on the stack and also attempts
93
     * translation using the Locale class.
94
     *
95
     * @param string|false $property property to filter by
96
     * @param string|false $locale   locale name to translate to
97
     *
98
     * @return array errors
99
     */
100
    public function all($property = false, $locale = false)
101
    {
102
        $errors = $this->stack;
103
        if ($property) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $property of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
104
            if (!isset($errors[$property])) {
105
                return [];
106
            }
107
108
            $errors = [$property => $this->stack[$property]];
109
        }
110
111
        // convert errors into messages
112
        $messages = [];
113
        foreach ($errors as $property => $errors2) {
114
            foreach ($errors2 as $error) {
115
                $messages[] = $this->parse($property, $error, $locale);
116
            }
117
        }
118
119
        return $messages;
120
    }
121
122
    /**
123
     * Checks if a property has an error.
124
     *
125
     * @param string $property
126
     *
127
     * @return bool
128
     */
129
    public function has($property)
130
    {
131
        return isset($this->stack[$property]);
132
    }
133
134
    /**
135
     * Clears all errors.
136
     *
137
     * @return self
138
     */
139
    public function clear()
140
    {
141
        $this->stack = [];
142
143
        return $this;
144
    }
145
146
    /**
147
     * Parses an error message before displaying it.
148
     *
149
     * @param string       $property
150
     * @param string       $error
151
     * @param string|false $locale
152
     *
153
     * @return array
154
     */
155
    private function parse($property, $error, $locale)
156
    {
157
        if (!$this->locale) {
158
            return $error;
159
        }
160
161
        $model = $this->model;
162
163
        $parameters = [
164
            'property' => $model::getPropertyTitle($property),
165
        ];
166
167
        return $this->locale->t($error, $parameters, $locale);
0 ignored issues
show
Bug introduced by
It seems like $locale defined by parameter $locale on line 155 can also be of type string; however, Infuse\Locale::t() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
168
    }
169
170
    //////////////////////////
171
    // IteratorAggregate Interface
172
    //////////////////////////
173
174
    public function getIterator()
175
    {
176
        return new ArrayIterator($this->stack);
177
    }
178
179
    //////////////////////////
180
    // Countable Interface
181
    //////////////////////////
182
183
    /**
184
     * Get total number of errors.
185
     *
186
     * @return int
187
     */
188
    public function count()
189
    {
190
        return count($this->all());
191
    }
192
193
    /////////////////////////////
194
    // ArrayAccess Interface
195
    /////////////////////////////
196
197
    public function offsetExists($offset)
198
    {
199
        return $this->has($offset);
200
    }
201
202
    public function offsetGet($offset)
203
    {
204
        return $this->all($offset);
205
    }
206
207
    public function offsetSet($offset, $error)
208
    {
209
        $this->add($offset, $error);
210
    }
211
212
    public function offsetUnset($offset)
213
    {
214
        if ($this->has($offset)) {
215
            unset($this->stack[$offset]);
216
        }
217
    }
218
}
219