Completed
Pull Request — master (#4)
by Mathieu
02:51
created

AbstractSource   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 422
Duplicated Lines 9.24 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 0
Metric Value
wmc 48
lcom 1
cbo 12
dl 39
loc 422
rs 8.4864
c 0
b 0
f 0

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A reset() 0 8 1
A setData() 0 13 3
A setProperties() 0 8 2
A properties() 0 4 1
A addProperty() 15 15 3
A createFilter() 0 5 1
A createFilterGroup() 0 5 1
A setOrders() 0 8 2
A orders() 0 4 1
D addOrder() 0 36 9
A createOrder() 0 5 1
A setPagination() 0 15 3
A pagination() 0 7 2
A createPagination() 0 5 1
A setPage() 0 10 2
A page() 0 4 1
A setNumPerPage() 0 10 2
A numPerPage() 0 4 1
A createConfig() 0 8 2
loadItem() 0 1 ?
loadItems() 0 1 ?
saveItem() 0 1 ?
updateItem() 0 1 ?
deleteItem() 0 1 ?
A getter() 12 12 3
A setter() 12 12 3
A camelize() 0 4 1
A pascalize() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AbstractSource often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractSource, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Charcoal\Source;
4
5
use InvalidArgumentException;
6
7
// From PSR-3
8
use Psr\Log\LoggerAwareInterface;
9
use Psr\Log\LoggerAwareTrait;
10
11
// From `charcoal-config`
12
use Charcoal\Config\ConfigurableInterface;
13
use Charcoal\Config\ConfigurableTrait;
14
15
// From `charcoal-core`
16
use Charcoal\Source\SourceConfig;
17
use Charcoal\Source\SourceInterface;
18
19
use Charcoal\Source\Filter;
20
use Charcoal\Source\FilterInterface;
21
use Charcoal\Source\FilterGroup;
22
use Charcoal\Source\FilterGroupInterface;
23
24
use Charcoal\Source\Order;
25
use Charcoal\Source\OrderInterface;
26
use Charcoal\Source\Pagination;
27
use Charcoal\Source\PaginationInterface;
28
use Charcoal\Source\FilterAwareTrait;
29
30
use Charcoal\Source\ModelAwareTrait;
31
32
/**
33
 * Full implementation, as abstract class, of the SourceInterface.
34
 */
35
abstract class AbstractSource implements
36
    SourceInterface,
37
    ConfigurableInterface,
38
    LoggerAwareInterface
39
{
40
    use ConfigurableTrait;
41
    use LoggerAwareTrait;
42
    use FilterAwareTrait;
43
    use ModelAwareTrait;
44
45
    /**
46
     * @var array $properties
47
     */
48
    private $properties = [];
49
50
    /**
51
     * Array of `Order` object
52
     * @var array $orders
53
     */
54
    protected $orders = [];
55
56
    /**
57
     * The `Pagination` object
58
     * @var Pagination|null $pagination
59
     */
60
    protected $pagination = null;
61
62
    /**
63
     * @param array|\ArrayAccess $dependencies The class dependencies.
64
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
65
     */
66
    public function __construct($dependencies)
67
    {
68
        $this->setLogger($dependencies['logger']);
69
    }
70
71
    /**
72
     * Reset everything but the model.
73
     *
74
     * @return AbstractSource Chainable
75
     */
76
    public function reset()
77
    {
78
        $this->properties = [];
79
        $this->filters = [];
80
        $this->orders = [];
81
        $this->pagination = null;
82
        return $this;
83
    }
84
85
    /**
86
     * Initialize the source's properties with an array of data.
87
     *
88
     * @param array $data The source data.
89
     * @return AbstractSource Chainable
90
     */
91
    public function setData(array $data)
92
    {
93
        foreach ($data as $prop => $val) {
94
            $func = [$this, $this->setter($prop)];
95
            if (is_callable($func)) {
96
                call_user_func($func, $val);
97
                unset($data[$prop]);
98
            } else {
99
                $this->{$prop} = $val;
100
            }
101
        }
102
        return $this;
103
    }
104
105
106
    /**
107
     * Set the properties of the source to fetch.
108
     *
109
     * This method accepts an array of property identifiers (property ident, as string)
110
     * that will, if supported, be fetched from the source.
111
     *
112
     * If no properties are set, it is assumed that all the Model's properties are to be fetched.
113
     *
114
     * @param array $properties The properties.
115
     * @return ColelectionLoader Chainable
116
     */
117
    public function setProperties(array $properties)
118
    {
119
        $this->properties = [];
120
        foreach ($properties as $p) {
121
            $this->addProperty($p);
122
        }
123
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Charcoal\Source\AbstractSource) is incompatible with the return type declared by the interface Charcoal\Source\SourceInterface::setProperties of type Charcoal\Source\ColelectionLoader.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
124
    }
125
126
    /**
127
     * @return array
128
     */
129
    public function properties()
130
    {
131
        return $this->properties;
132
    }
133
134
    /**
135
     * @param string $property Property ident.
136
     * @throws InvalidArgumentException If property is not a string or empty.
137
     * @return CollectionLoader Chainable
138
     */
139 View Code Duplication
    public function addProperty($property)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
    {
141
        if (!is_string($property)) {
142
            throw new InvalidArgumentException(
143
                'Property must be a string.'
144
            );
145
        }
146
        if ($property=='') {
147
            throw new InvalidArgumentException(
148
                'Property can not be empty.'
149
            );
150
        }
151
        $this->properties[] = $property;
152
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Charcoal\Source\AbstractSource) is incompatible with the return type declared by the interface Charcoal\Source\SourceInterface::addProperty of type Charcoal\Source\CollectionLoader.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
153
    }
154
155
    /**
156
     * @return FilterInterface
157
     */
158
    protected function createFilter()
159
    {
160
        $filter = new Filter();
161
        return $filter;
162
    }
163
164
165
    /**
166
     * @return FilterGroupInterface
167
     */
168
    protected function createFilterGroup()
169
    {
170
        $filterGroup = new FilterGroup();
171
        return $filterGroup;
172
    }
173
174
    /**
175
     * @param array $orders The orders to set.
176
     * @return CollectionLoader Chainable
177
     */
178
    public function setOrders(array $orders)
179
    {
180
        $this->orders = [];
181
        foreach ($orders as $o) {
182
            $this->addOrder($o);
183
        }
184
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Charcoal\Source\AbstractSource) is incompatible with the return type declared by the interface Charcoal\Source\SourceInterface::setOrders of type Charcoal\Source\CollectionLoader.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
185
    }
186
187
    /**
188
     * @return array
189
     */
190
    public function orders()
191
    {
192
        return $this->orders;
193
    }
194
195
    /**
196
     * @param string|array|Order $param        The order property, or an Order object / array.
197
     * @param string             $mode         Optional.
198
     * @param array              $orderOptions Optional.
199
     * @throws InvalidArgumentException If the param argument is invalid.
200
     * @return CollectionLoader Chainable
201
     */
202
    public function addOrder($param, $mode = 'asc', array $orderOptions = null)
203
    {
204
        if ($param instanceof OrderInterface) {
205
            $order = $param;
206
        } elseif (is_array($param)) {
207
            $order = $this->createOrder();
208
            $order->setData($param);
209
        } elseif (is_string($param)) {
210
            $order = $this->createOrder();
211
            $order->setProperty($param);
212
            $order->setMode($mode);
213
            if (isset($orderOptions['values'])) {
214
                $order->setValues($orderOptions['values']);
215
            }
216
        } else {
217
            throw new InvalidArgumentException(
218
                'Parameter must be an OrderInterface object or a property ident.'
219
            );
220
        }
221
222
        if ($this->hasModel()) {
223
            $property = $order->property();
224
            if ($property) {
225
                $p = $this->model()->p($property);
226
                if ($p) {
227
                    if ($p->l10n()) {
228
                        $order->setProperty($p->l10nIdent());
229
                    }
230
                }
231
            }
232
        }
233
234
        $this->orders[] = $order;
235
236
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Charcoal\Source\AbstractSource) is incompatible with the return type declared by the interface Charcoal\Source\SourceInterface::addOrder of type Charcoal\Source\CollectionLoader.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
237
    }
238
239
    /**
240
     * @return OrderInterface
241
     */
242
    protected function createOrder()
243
    {
244
        $order = new Order();
245
        return $order;
246
    }
247
248
    /**
249
     * @param mixed $param The pagination object or array.
250
     * @throws InvalidArgumentException If the argument is not an object or array.
251
     * @return CollectionLoader Chainable
252
     */
253
    public function setPagination($param)
254
    {
255
        if ($param instanceof PaginationInterface) {
256
            $this->pagination = $param;
0 ignored issues
show
Documentation Bug introduced by
It seems like $param of type object<Charcoal\Source\PaginationInterface> is incompatible with the declared type object<Charcoal\Source\Pagination>|null of property $pagination.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
257
        } elseif (is_array($param)) {
258
            $pagination = $this->createPagination();
259
            $pagination->setData($param);
260
            $this->pagination = $pagination;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pagination of type object<Charcoal\Source\PaginationInterface> is incompatible with the declared type object<Charcoal\Source\Pagination>|null of property $pagination.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
261
        } else {
262
            throw new InvalidArgumentException(
263
                'Can not set pagination, invalid argument.'
264
            );
265
        }
266
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Charcoal\Source\AbstractSource) is incompatible with the return type declared by the interface Charcoal\Source\SourceInterface::setPagination of type Charcoal\Source\CollectionLoader.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
267
    }
268
269
    /**
270
     * Get the pagination object.
271
     *
272
     * If the pagination wasn't set previously, a new (default / blank) Pagination object will be created.
273
     * (Always return a `PaginationInterface` object)
274
     *
275
     * @return Pagination
276
     */
277
    public function pagination()
278
    {
279
        if ($this->pagination === null) {
280
            $this->pagination = $this->createPagination();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createPagination() of type object<Charcoal\Source\PaginationInterface> is incompatible with the declared type object<Charcoal\Source\Pagination>|null of property $pagination.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
281
        }
282
        return $this->pagination;
283
    }
284
285
    /**
286
     * @return PaginationInterface
287
     */
288
    protected function createPagination()
289
    {
290
        $pagination = new Pagination();
291
        return $pagination;
292
    }
293
294
    /**
295
     * @param integer $page The page number.
296
     * @throws InvalidArgumentException If the page argument is not numeric.
297
     * @return CollectionLoader Chainable
298
     */
299
    public function setPage($page)
300
    {
301
        if (!is_numeric($page)) {
302
            throw new InvalidArgumentException(
303
                'Page must be an integer.'
304
            );
305
        }
306
        $this->pagination()->setPage((int)$page);
307
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Charcoal\Source\AbstractSource) is incompatible with the return type declared by the interface Charcoal\Source\SourceInterface::setPage of type Charcoal\Source\CollectionLoader.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
308
    }
309
310
    /**
311
     * @return integer
312
     */
313
    public function page()
314
    {
315
        return $this->pagination()->page();
316
    }
317
318
    /**
319
     * @param integer $num The number of items to retrieve per page.
320
     * @throws InvalidArgumentException If the num per page argument is not numeric.
321
     * @return CollectionLoader Chainable
322
     */
323
    public function setNumPerPage($num)
324
    {
325
        if (!is_numeric($num)) {
326
            throw new InvalidArgumentException(
327
                'Num must be an integer.'
328
            );
329
        }
330
        $this->pagination()->setNumPerPage((int)$num);
331
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Charcoal\Source\AbstractSource) is incompatible with the return type declared by the interface Charcoal\Source\SourceInterface::setNumPerPage of type Charcoal\Source\CollectionLoader.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
332
    }
333
334
    /**
335
     * @return integer
336
     */
337
    public function numPerPage()
338
    {
339
        return $this->pagination()->numPerPage();
340
    }
341
342
    /**
343
     * ConfigurableTrait > createConfig()
344
     *
345
     * @param array $data Optional.
346
     * @return SourceConfig
347
     */
348
    public function createConfig(array $data = null)
349
    {
350
        $config = new SourceConfig();
351
        if (is_array($data)) {
352
            $config->merge($data);
0 ignored issues
show
Unused Code introduced by
The call to the method Charcoal\Source\SourceConfig::merge() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
353
        }
354
        return $config;
355
    }
356
357
    /**
358
     * @param mixed             $ident The ID of the item to load.
359
     * @param StorableInterface $item  Optional item to load into.
360
     * @return StorableInterface
361
     */
362
    abstract public function loadItem($ident, StorableInterface $item = null);
363
364
    /**
365
     * @param StorableInterface|null $item The model to load items from.
366
     * @return array
367
     */
368
    abstract public function loadItems(StorableInterface $item = null);
369
370
    /**
371
     * Save an item (create a new row) in storage.
372
     *
373
     * @param StorableInterface $item The object to save.
374
     * @return mixed The created item ID, or false in case of an error.
375
     */
376
    abstract public function saveItem(StorableInterface $item);
377
378
    /**
379
     * Update an item in storage.
380
     *
381
     * @param StorableInterface $item       The object to update.
382
     * @param array             $properties The list of properties to update, if not all.
383
     * @return boolean Success / Failure
384
     */
385
    abstract public function updateItem(StorableInterface $item, array $properties = null);
386
387
    /**
388
     * Delete an item from storage
389
     *
390
     * @param StorableInterface $item Optional item to delete. If none, the current model object will be used..
391
     * @return boolean Success / Failure
392
     */
393
    abstract public function deleteItem(StorableInterface $item = null);
394
395
    /**
396
     * Allow an object to define how the key getter are called.
397
     *
398
     * @param string $key  The key to get the getter from.
399
     * @param string $case Optional. The type of case to return. camel, pascal or snake.
400
     * @return string The getter method name, for a given key.
401
     */
402 View Code Duplication
    protected function getter($key, $case = 'camel')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
403
    {
404
        $getter = $key;
405
406
        if ($case == 'camel') {
407
            return $this->camelize($getter);
408
        } elseif ($case == 'pascal') {
409
            return $this->pascalize($getter);
410
        } else {
411
            return $getter;
412
        }
413
    }
414
415
    /**
416
     * Allow an object to define how the key setter are called.
417
     *
418
     * @param string $key  The key to get the setter from.
419
     * @param string $case Optional. The type of case to return. camel, pascal or snake.
420
     * @return string The setter method name, for a given key.
421
     */
422 View Code Duplication
    protected function setter($key, $case = 'camel')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
423
    {
424
        $setter = 'set_'.$key;
425
426
        if ($case == 'camel') {
427
            return $this->camelize($setter);
428
        } elseif ($case == 'pascal') {
429
            return $this->pascalize($setter);
430
        } else {
431
            return $setter;
432
        }
433
    }
434
435
    /**
436
     * Transform a snake_case string to camelCase.
437
     *
438
     * @param string $str The snake_case string to camelize.
439
     * @return string The camelCase string.
440
     */
441
    private function camelize($str)
442
    {
443
        return lcfirst($this->pascalize($str));
444
    }
445
446
    /**
447
     * Transform a snake_case string to PamelCase.
448
     *
449
     * @param string $str The snake_case string to pascalize.
450
     * @return string The PamelCase string.
451
     */
452
    private function pascalize($str)
453
    {
454
        return implode('', array_map('ucfirst', explode('_', $str)));
455
    }
456
}
457