Completed
Pull Request — master (#308)
by Sergey
06:18
created

ProvidersContainer::wait()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace seregazhuk\PinterestBot\Api;
4
5
use seregazhuk\PinterestBot\Api\Providers\Pins;
6
use seregazhuk\PinterestBot\Api\Providers\User;
7
use seregazhuk\PinterestBot\Api\Providers\Auth;
8
use seregazhuk\PinterestBot\Api\Providers\Inbox;
9
use seregazhuk\PinterestBot\Api\Providers\Boards;
10
use seregazhuk\PinterestBot\Api\Providers\Topics;
11
use seregazhuk\PinterestBot\Api\Providers\Pinners;
12
use seregazhuk\PinterestBot\Api\Providers\Keywords;
13
use seregazhuk\PinterestBot\Api\Providers\Comments;
14
use seregazhuk\PinterestBot\Api\Providers\Password;
15
use seregazhuk\PinterestBot\Api\Providers\Interests;
16
use seregazhuk\PinterestBot\Exceptions\WrongProvider;
17
use seregazhuk\PinterestBot\Api\Contracts\HttpClient;
18
use seregazhuk\PinterestBot\Api\Providers\Core\Provider;
19
use seregazhuk\PinterestBot\Api\Providers\Core\ProviderWrapper;
20
21
/**
22
 * @property Pins $pins
23
 * @property Inbox $inbox
24
 * @property User $user
25
 * @property Boards $boards
26
 * @property Pinners $pinners
27
 * @property Keywords $keywords
28
 * @property Interests $interests
29
 * @property Topics $topics
30
 * @property Auth $auth
31
 * @property Comments $comments
32
 * @property Password $password
33
 *
34
 * Class ProvidersContainer
35
 * @package seregazhuk\PinterestBot\Api
36
 */
37
class ProvidersContainer
38
{
39
    const PROVIDERS_NAMESPACE = 'seregazhuk\\PinterestBot\\Api\\Providers\\';
40
41
    /**
42
     * References to the request that travels
43
     * through the application.
44
     *
45
     * @var Request
46
     */
47
    protected $request;
48
49
    /**
50
     * @var Response
51
     */
52
    protected $response;
53
54
    /**
55
     * A array containing the cached providers.
56
     *
57
     * @var array
58
     */
59
    protected $providers = [];
60
61
    /**
62
     * @param Request $request
63
     * @param Response $response
64
     */
65
    public function __construct(Request $request, Response $response)
66
    {
67
        $this->request = $request;
68
        $this->response = $response;
69
    }
70
71
72
    /**
73
     * Magic method to access different providers from the container.
74
     *
75
     * @param string $provider
76
     * @return Provider
77
     */
78
    public function __get($provider)
79
    {
80
        return $this->getProvider($provider);
81
    }
82
83
84
    /**
85
     * Gets provider object by name. If there is no such provider
86
     * in providers array, it will try to create it, then save
87
     * it, and then return.
88
     *
89
     * @param string $provider
90
     *
91
     * @throws WrongProvider
92
     *
93
     * @return Provider
94
     */
95
    public function getProvider($provider)
96
    {
97
        $provider = strtolower($provider);
98
99
        // Check if an instance has already been initiated. If not
100
        // build it and then add to the providers array.
101
        if (!isset($this->providers[$provider])) {
102
            $this->addProvider($provider);
103
        }
104
105
        return $this->providers[$provider];
106
    }
107
108
    /**
109
     * Creates provider by class name, and if success saves
110
     * it to providers array. Provider class must exist in PROVIDERS_NAMESPACE.
111
     *
112
     * @param string $provider
113
     * @throws WrongProvider
114
     */
115
    protected function addProvider($provider)
116
    {
117
        $className = $this->resolveProviderClass($provider);
118
119
        $this->providers[$provider] = $this->buildProvider($className);
120
    }
121
122
    /**
123
     * Build Provider object.
124
     *
125
     * @param string $className
126
     * @throws WrongProvider
127
     * @return ProviderWrapper
128
     */
129
    protected function buildProvider($className)
130
    {
131
        $provider = new $className($this);
132
133
        return new ProviderWrapper($provider);
134
    }
135
136
    /**
137
     * Proxies call to Request object and returns message from
138
     * the error object.
139
     *
140
     * @return string|null
141
     */
142
    public function getLastError()
143
    {
144
        $error = $this->response->getLastError();
145
146
        if(isset($error['message'])) return $error['message'];
147
148
        if(isset($error['code'])) return $error['code'];
149
150
        return null;
151
    }
152
153
    /**
154
     * Returns client context from Pinterest response. By default info returns from the last
155
     * Pinterest response. If there was no response before or the argument $reload is
156
     * true, we make a dummy request to the main page to update client context.
157
     *
158
     * @param bool $reload
159
     * @return array|null
160
     */
161
    public function getClientInfo($reload = false)
162
    {
163
        $clientInfo = $this->response->getClientInfo();
164
165
        if(is_null($clientInfo) || $reload) {
166
            $this->getProvider('user')->visitPage();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class seregazhuk\PinterestBot\...Providers\Core\Provider as the method visitPage() does only exist in the following sub-classes of seregazhuk\PinterestBot\...Providers\Core\Provider: seregazhuk\PinterestBot\Api\Providers\User. 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...
167
        }
168
169
        return $this->response->getClientInfo();
170
    }
171
172
    /**
173
     * Returns HttpClient object for setting user-agent string or
174
     * other CURL available options.
175
     *
176
     * @return HttpClient
177
     */
178
    public function getHttpClient()
179
    {
180
        return $this->request->getHttpClient();
181
    }
182
183
    /**
184
     * @param string $provider
185
     * @return string
186
     * @throws WrongProvider
187
     */
188
    protected function resolveProviderClass($provider)
189
    {
190
        $className = self::PROVIDERS_NAMESPACE . ucfirst($provider);
191
192
        if (!$this->checkIsProviderClass($className)) {
193
            throw new WrongProvider("Provider $className not found.");
194
        }
195
196
        return $className;
197
    }
198
199
    /**
200
     * @param string $className
201
     * @return bool
202
     */
203
    protected function checkIsProviderClass($className)
204
    {
205
        if(!class_exists($className)) return false;
206
207
        return in_array(Provider::class, class_parents($className));
208
    }
209
210
    /**
211
     * @return Request
212
     */
213
    public function getRequest()
214
    {
215
        return $this->request;
216
    }
217
218
    /**
219
     * @return Response
220
     */
221
    public function getResponse()
222
    {
223
        return $this->response;
224
    }
225
}
226