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