Model   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 97.96%

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 0
dl 0
loc 146
ccs 48
cts 49
cp 0.9796
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B fromArray() 0 32 10
A fromJson() 0 5 1
A toArray() 0 4 1
A toJson() 0 4 1
C toArrayRecursive() 0 36 14
1
<?php
2
/**
3
 * Yandex PHP Library
4
 *
5
 * @copyright NIX Solutions Ltd.
6
 * @link https://github.com/nixsolutions/yandex-php-library
7
 */
8
9
/**
10
 * @namespace
11
 */
12
namespace Yandex\Common;
13
14
/**
15
 * Class Model
16
 * @package Yandex\Common
17
 */
18
abstract class Model
19
{
20
    protected $mappingClasses = [];
21
22
    /**
23
     * Contains property name mappings.
24
     *
25
     * [
26
     *  'data_array_property1' => 'objectProperty1',
27
     *  'data_array_property2' => 'objectProperty2',
28
     * ]
29
     *
30
     * Data array property uses as keys
31
     * because there is can be more then one rule per object property
32
     *
33
     * f.g. $data['nmodels'] and ['modelsnum'] should map in modelsCount property.
34
     * Otherwise not unique array keys cause remapping of properties.
35
     *
36
     * @var array
37
     */
38
    protected $propNameMap = [];
39
40
    /**
41
     * Constructor
42
     *
43
     * @param array $data
44
     */
45 174
    public function __construct($data = [])
46
    {
47 174
        $this->fromArray($data);
48 174
    }
49
50
    /**
51
     * Set from array
52
     *
53
     * @param array $data
54
     * @return $this
55
     */
56 172
    public function fromArray($data)
57
    {
58 172
        foreach ($data as $key => $val) {
59 136
            if (is_int($key)) {
60 80
                if (method_exists($this, "add")) {
61 80
                    $this->add($val);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Yandex\Common\Model as the method add() does only exist in the following sub-classes of Yandex\Common\Model: Yandex\DataSync\Models\Database\Delta\RecordFields, Yandex\DataSync\Models\Database\Delta\Records, Yandex\DataSync\Models\Database\Deltas, Yandex\DataSync\Models\Databases, Yandex\Market\Content\Models\Base\Models, Yandex\Market\Content\Models\Categories, Yandex\Market\Content\Models\Children, Yandex\Market\Content\Models\Comments, Yandex\Market\Content\Models\Contras, Yandex\Market\Content\Models\DeliveryMethods, Yandex\Market\Content\Models\Filters, Yandex\Market\Content\Models\GeoRegions, Yandex\Market\Content\Models\ModelOpinions, Yandex\Market\Content\Models\ModelVisualPhotos, Yandex\Market\Content\Models\OfferPhotos, Yandex\Market\Content\Models\Offers, Yandex\Market\Content\Models\Options, Yandex\Market\Content\Models\Outlets, Yandex\Market\Content\Models\Photos, Yandex\Market\Content\Models\Pros, Yandex\Market\Content\Models\Reviews, Yandex\Market\Content\Models\Schedules, Yandex\Market\Content\Models\SearchResults, Yandex\Market\Content\Models\ShopOpinions, Yandex\Market\Content\Models\Shops, Yandex\Market\Content\Models\Vendors, Yandex\Market\Partner\Models\Campaigns, Yandex\Market\Partner\Models\DeliveryOptions, Yandex\Market\Partner\Models\DeliveryRules, Yandex\Market\Partner\Models\Items, Yandex\Market\Partner\Models\MarketModelOffers, Yandex\Market\Partner\Models\MarketModels, Yandex\Market\Partner\Models\Orders, Yandex\Market\Partner\Models\Outlets, Yandex\Market\Partner\Models\StateReasons, Yandex\Market\Partner\Models\Stats, Yandex\Metrica\Analytics\Models\ColumnHeaders, Yandex\Metrica\Management\Models\Accounts, Yandex\Metrica\Management\Models\Conditions, Yandex\Metrica\Management\Models\Counters, Yandex\Metrica\Management\Models\Delegates, Yandex\Metrica\Management\Models\Filters, Yandex\Metrica\Management\Models\Goals, Yandex\Metrica\Management\Models\Grants, Yandex\Metrica\Management\Models\Operations, Yandex\Metrica\Stat\Models\ComparisonData, Yandex\Metrica\Stat\Models\Data, Yandex\Metrica\Stat\Models\Dimensions, Yandex\Metrica\Stat\Models\DrillDownComparisonData, Yandex\Metrica\Stat\Models\DrillDownData. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
62
                }
63
            }
64
65 136
            $propertyName = $key;
66 136
            $ourPropertyName = array_search($propertyName, $this->propNameMap);
67
68 136
            if ($ourPropertyName && isset($data[$ourPropertyName])) {
69
                $propertyName = $ourPropertyName;
70
            }
71
72 136
            if (!empty($this->propNameMap)) {
73 79
                if (array_key_exists($key, $this->propNameMap)) {
74 73
                    $propertyName = $this->propNameMap[$key];
75
                }
76
            }
77
78 136
            if (property_exists($this, $propertyName)) {
79 128
                if (isset($this->mappingClasses[$propertyName])) {
80 100
                    $this->{$propertyName} = new $this->mappingClasses[$propertyName]($val);
81
                } else {
82 136
                    $this->{$propertyName} = $val;
83
                }
84
            }
85
        }
86 172
        return $this;
87
    }
88
89
    /**
90
     * Set from json
91
     *
92
     * @param string $json
93
     * @return $this
94
     */
95 1
    public function fromJson($json)
96
    {
97 1
        $this->fromArray(json_decode($json, true));
98 1
        return $this;
99
    }
100
101
    /**
102
     * Get array from object
103
     *
104
     * @return array
105
     */
106 43
    public function toArray()
107
    {
108 43
        return $this->toArrayRecursive($this);
109
    }
110
111
    /**
112
     * Get array from object
113
     *
114
     * @return string
115
     */
116 1
    public function toJson()
117
    {
118 1
        return json_encode($this->toArrayRecursive($this));
119
    }
120
121
    /**
122
     * Get array from object
123
     *
124
     * @param array|object $data
125
     * @return array
126
     */
127 41
    protected function toArrayRecursive($data)
128
    {
129 41
        if (is_array($data) || is_object($data)) {
130 41
            $result = [];
131 41
            foreach ($data as $key => $value) {
132 39
                if ($key === "mappingClasses" || $key === "propNameMap") {
133 39
                    continue;
134
                }
135 39
                $propNameMap = $key;
136 39
                $obj         = $this;
137 39
                if (is_object($data)) {
138 39
                    $obj = $data;
139
                }
140
141 39
                if (property_exists($obj, $propNameMap)) {
142 39
                    $ourPropertyName = array_search($propNameMap, $obj->propNameMap);
143
144 39
                    if ($ourPropertyName) {
145 28
                        $propNameMap = $ourPropertyName;
146
                    }
147
                }
148
149 39
                if (is_object($value) && method_exists($value, "getAll")) {
150 9
                    if (method_exists($obj, 'toArrayRecursive')) {
151 9
                        $result[$propNameMap] = $obj->toArrayRecursive($value->getAll());
152
                    }
153 39
                } elseif ($value !== null) {
154 18
                    if (method_exists($obj, 'toArrayRecursive')) {
155 39
                        $result[$propNameMap] = $obj->toArrayRecursive($value);
156
                    }
157
                }
158
            }
159 41
            return $result;
160
        }
161 18
        return $data;
162
    }
163
}
164