Passed
Push — master ( bf3918...698edc )
by Gabor
12:58 queued 06:03
created

UserMetaStorage::getUserMetaSetForUserId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2017 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
declare(strict_types = 1);
13
14
namespace WebHemi\Data\Storage\User;
15
16
use WebHemi\Data\EntityInterface;
17
use WebHemi\Data\Entity\User\UserMetaEntity;
18
use WebHemi\Data\Storage\AbstractStorage;
19
use WebHemi\DateTime;
20
use WebHemi\StringLib;
21
22
/**
23
 * Class UserMetaStorage.
24
 */
25
class UserMetaStorage extends AbstractStorage
26
{
27
    /** @var string */
28
    protected $dataGroup = 'webhemi_user_meta';
29
    /** @var string */
30
    protected $idKey = 'id_user_meta';
31
    /** @var string */
32
    private $userId = 'fk_user';
33
    /** @var string */
34
    private $metaKey = 'meta_key';
35
    /** @var string */
36
    private $metaData = 'meta_data';
37
    /** @var string */
38
    private $dateCreated = 'date_created';
39
    /** @var string */
40
    private $dateModified = 'date_modified';
41
42
    /**
43
     * Populates an entity with storage data.
44
     *
45
     * @param EntityInterface $dataEntity
46
     * @param array           $data
47
     * @return void
48
     */
49 1
    protected function populateEntity(EntityInterface&$dataEntity, array $data) : void
50
    {
51
        /* @var UserMetaEntity $dataEntity */
52 1
        $dataEntity->setUserMetaId((int) $data[$this->idKey])
53 1
            ->setUserId((int) $data[$this->userId])
54 1
            ->setMetaKey($data[$this->metaKey])
55 1
            ->setMetaData($data[$this->metaData])
56 1
            ->setDateCreated(new DateTime($data[$this->dateCreated] ?? 'now'))
57 1
            ->setDateModified(!empty($data[$this->dateModified]) ? new DateTime($data[$this->dateModified]) : null);
58 1
    }
59
60
    /**
61
     * Get data from an entity.
62
     *
63
     * @param EntityInterface $dataEntity
64
     * @return array
65
     */
66 1
    protected function getEntityData(EntityInterface $dataEntity) : array
67
    {
68
        /** @var UserMetaEntity $dataEntity */
69 1
        $dateCreated = $dataEntity->getDateCreated();
70 1
        $dateModified = $dataEntity->getDateModified();
71
72
        return [
73 1
            $this->idKey => $dataEntity->getKeyData(),
74 1
            $this->userId => $dataEntity->getUserId(),
75 1
            $this->metaKey => $dataEntity->getMetaKey(),
76 1
            $this->metaData => $dataEntity->getMetaData(),
77 1
            $this->dateCreated => $dateCreated instanceof DateTime ? $dateCreated->format('Y-m-d H:i:s') : null,
78 1
            $this->dateModified => $dateModified instanceof DateTime ? $dateModified->format('Y-m-d H:i:s') : null
79
        ];
80
    }
81
82
    /**
83
     * Returns a User Meta entity identified by (unique) ID.
84
     *
85
     * @param int $identifier
86
     * @return null|UserMetaEntity
87
     */
88 1
    public function getUserMetaById(int $identifier) : ? UserMetaEntity
89
    {
90
        /** @var null|UserMetaEntity $dataEntity */
91 1
        $dataEntity = $this->getDataEntity([$this->idKey => $identifier]);
92
93 1
        return $dataEntity;
94
    }
95
96
    /**
97
     * Returns a User Meta data list identified by user ID.
98
     *
99
     * @param int $userId
100
     * @return array
101
     */
102
    public function getUserMetaArrayForUserId(int $userId) : array
103
    {
104
        $userMetaEntitySet = $this->getDataEntitySet([$this->userId => $userId]);
105
        $userMetaSet = [];
106
107
        /** @var UserMetaEntity $metaEntity */
108
        foreach ($userMetaEntitySet as $metaEntity) {
109
            $data = $this->processMetaEntity($metaEntity);
0 ignored issues
show
Compatibility introduced by
$metaEntity of type object<WebHemi\Data\EntityInterface> is not a sub-type of object<WebHemi\Data\Entity\User\UserMetaEntity>. It seems like you assume a concrete implementation of the interface WebHemi\Data\EntityInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
110
            $userMetaSet[$data['key']] = $data['value'];
111
        }
112
113
        return $userMetaSet;
114
    }
115
116
    /**
117
     * Processes a user meta entity.
118
     *
119
     * @param UserMetaEntity $metaEntity
120
     * @return array
121
     */
122
    private function processMetaEntity(UserMetaEntity $metaEntity) : array
123
    {
124
125
        $key = $metaEntity->getMetaKey();
126
        $data = $metaEntity->getMetaData();
127
128
        if ($key == 'avatar' && strpos($data, 'gravatar://') === 0) {
129
            $data = str_replace('gravatar://', '', $data);
130
            $data = 'http://www.gravatar.com/avatar/'.md5(strtolower($data)).'?s=256&r=g';
131
        }
132
133
        $jsonDataKeys = ['workplaces', 'instant_messengers', 'phone_numbers', 'social_networks', 'websites'];
134
135
        if (in_array($key, $jsonDataKeys) && !empty($data)) {
136
            $data = json_decode($data, true);
137
        }
138
139
        $data = [
140
            'key' => lcfirst(StringLib::convertUnderscoreToCamelCase($key)),
141
            'value' => $data
142
        ];
143
144
        return $data;
145
    }
146
147
    /**
148
     * Returns a User Meta entity list identified by user ID.
149
     *
150
     * @param int  $userId
151
     * @param bool $keysAsKeys -  whether to use the meta keys for the returning array too
152
     * @return UserMetaEntity[]
153
     */
154
    public function getUserMetaEntitySetForUserId(int $userId, bool $keysAsKeys = false) : array
155
    {
156
        $metaSet = $this->getDataEntitySet([$this->userId => $userId]);
157
158
        if ($keysAsKeys) {
159
            $tmp = [];
160
            /** @var UserMetaEntity $userMetaEntity */
161
            foreach ($metaSet as $userMetaEntity) {
162
                $tmp[$userMetaEntity->getMetaKey()] = $userMetaEntity;
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface WebHemi\Data\EntityInterface as the method getMetaKey() does only exist in the following implementations of said interface: WebHemi\Data\Entity\User\UserMetaEntity.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
163
            }
164
            $metaSet = $tmp;
165
            unset($tmp);
166
        }
167
168
        return $metaSet;
169
    }
170
}
171