These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace Jasny\Codeception; |
||
| 4 | |||
| 5 | use Jasny\Router; |
||
| 6 | use Codeception\Configuration; |
||
| 7 | use Codeception\Lib\Framework; |
||
| 8 | use Codeception\TestInterface; |
||
| 9 | use Jasny\Codeception\Connector; |
||
| 10 | use Psr\Http\Message\ServerRequestInterface; |
||
| 11 | use Psr\Http\Message\ResponseInterface; |
||
| 12 | use Interop\Container\ContainerInterface; |
||
| 13 | |||
| 14 | /** |
||
| 15 | * Module for running functional tests using Jasny MVC |
||
| 16 | */ |
||
| 17 | class Module extends Framework |
||
| 18 | { |
||
| 19 | /** |
||
| 20 | * Required configuration fields |
||
| 21 | * @var array |
||
| 22 | */ |
||
| 23 | protected $requiredFields = ['container']; |
||
| 24 | |||
| 25 | /** |
||
| 26 | * @var Router |
||
| 27 | */ |
||
| 28 | public $router; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * @var ServerRequestInterface |
||
| 32 | */ |
||
| 33 | public $baseRequest; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * @var ResponseInterface |
||
| 37 | */ |
||
| 38 | public $baseResponse; |
||
| 39 | |||
| 40 | |||
| 41 | /** |
||
| 42 | * Load the container by including the file. |
||
| 43 | * @codeCoverageIgnore |
||
| 44 | * |
||
| 45 | * @param string $file |
||
| 46 | * @return ContainerInterface |
||
| 47 | */ |
||
| 48 | protected function loadContainer($file) |
||
| 49 | { |
||
| 50 | return include $file; |
||
| 51 | } |
||
| 52 | |||
| 53 | /** |
||
| 54 | * Get the container. |
||
| 55 | * |
||
| 56 | * @return ContainerInterface |
||
| 57 | */ |
||
| 58 | 3 | protected function initContainer() |
|
| 59 | { |
||
| 60 | 3 | $container = $this->loadContainer(Configuration::projectDir() . $this->config['container']); |
|
| 61 | |||
| 62 | 3 | if (!$container instanceof ContainerInterface) { |
|
| 63 | 1 | throw new \UnexpectedValueException("Failed to get a container from '{$this->config['container']}'"); |
|
| 64 | } |
||
| 65 | |||
| 66 | 2 | return $container; |
|
| 67 | } |
||
| 68 | |||
| 69 | /** |
||
| 70 | * Check if the response writes to the output buffer |
||
| 71 | * |
||
| 72 | * @return boolean |
||
| 73 | */ |
||
| 74 | 7 | protected function usesOutputBuffer() |
|
| 75 | { |
||
| 76 | 7 | return isset($this->baseResponse) && $this->baseResponse->getBody()->getMetadata('uri') === 'php://output'; |
|
| 77 | } |
||
| 78 | |||
| 79 | /** |
||
| 80 | * Enable output buffering |
||
| 81 | * |
||
| 82 | * @throws \RuntimeException |
||
| 83 | */ |
||
| 84 | 2 | protected function startOutputBuffering() |
|
| 85 | { |
||
| 86 | 2 | if ($this->obGetLevel() === 0) { |
|
| 87 | 2 | $this->obStart(); |
|
| 88 | 2 | } |
|
| 89 | |||
| 90 | 2 | if ($this->obGetLevel() < 1) { |
|
| 91 | 1 | throw new \RuntimeException("Failed to start output buffering"); |
|
| 92 | } |
||
| 93 | 1 | } |
|
| 94 | |||
| 95 | /** |
||
| 96 | * Disable output buffering |
||
| 97 | */ |
||
| 98 | 1 | protected function stopOutputBuffering() |
|
| 99 | { |
||
| 100 | 1 | $this->obClean(); |
|
| 101 | 1 | } |
|
| 102 | |||
| 103 | |||
| 104 | /** |
||
| 105 | * Initialize the module |
||
| 106 | */ |
||
| 107 | 3 | public function _initialize() |
|
| 108 | { |
||
| 109 | 3 | $container = $this->initContainer(); |
|
| 110 | |||
| 111 | 2 | $this->router = $container->get(Router::class); |
|
| 112 | |||
| 113 | 2 | if ($container->has(ServerRequestInterface::class)) { |
|
| 114 | 1 | $this->baseRequest = $container->get(ServerRequestInterface::class); |
|
| 115 | 1 | } |
|
| 116 | |||
| 117 | 2 | if ($container->has(ResponseInterface::class)) { |
|
| 118 | 1 | $this->baseResponse = $container->get(ResponseInterface::class); |
|
| 119 | 1 | } |
|
| 120 | 2 | } |
|
| 121 | |||
| 122 | /** |
||
| 123 | * Call before suite |
||
| 124 | * |
||
| 125 | * @param array $settings |
||
| 126 | */ |
||
| 127 | 4 | public function _beforeSuite($settings = []) |
|
| 128 | { |
||
| 129 | 4 | parent::_beforeSuite($settings); |
|
| 130 | |||
| 131 | 4 | if ($this->usesOutputBuffer()) { |
|
| 132 | 2 | $this->startOutputBuffering(); |
|
| 133 | 1 | } |
|
| 134 | 3 | } |
|
| 135 | |||
| 136 | /** |
||
| 137 | * Call after suite |
||
| 138 | */ |
||
| 139 | 3 | public function _afterSuite() |
|
| 140 | { |
||
| 141 | 3 | if ($this->usesOutputBuffer()) { |
|
| 142 | 1 | $this->stopOutputBuffering(); |
|
| 143 | 1 | } |
|
| 144 | |||
| 145 | 3 | parent::_afterSuite(); |
|
| 146 | 3 | } |
|
| 147 | |||
| 148 | /** |
||
| 149 | * Before each test |
||
| 150 | * |
||
| 151 | * @param TestInterface $test |
||
| 152 | */ |
||
| 153 | 4 | public function _before(TestInterface $test) |
|
| 154 | { |
||
| 155 | 4 | $this->client = new Connector(); |
|
| 156 | 4 | $this->client->setRouter($this->router); |
|
| 157 | |||
| 158 | 4 | if (isset($this->baseRequest)) { |
|
| 159 | 2 | $this->client->setBaseRequest($this->baseRequest); |
|
| 160 | 2 | } |
|
| 161 | |||
| 162 | 4 | if (isset($this->baseResponse)) { |
|
| 163 | 2 | $this->client->setBaseResponse($this->baseResponse); |
|
| 164 | 2 | } |
|
| 165 | |||
| 166 | 4 | parent::_before($test); |
|
| 167 | 4 | } |
|
| 168 | |||
| 169 | /** |
||
| 170 | * After each test |
||
| 171 | * |
||
| 172 | * @param TestInterface $test |
||
| 173 | */ |
||
| 174 | 3 | public function _after(TestInterface $test) |
|
| 175 | { |
||
| 176 | 3 | if ($this->sessionStatus() === PHP_SESSION_ACTIVE) { |
|
| 177 | 1 | $this->sessionAbort(); |
|
| 178 | 1 | } |
|
| 179 | |||
| 180 | 3 | if (isset($this->client)) { |
|
| 181 | 1 | $this->client->reset(); |
|
|
0 ignored issues
–
show
|
|||
| 182 | |||
| 183 | 1 | if (isset($this->baseRequest)) { |
|
| 184 | 1 | $this->baseRequest = $this->client->getBaseRequest(); |
|
|
0 ignored issues
–
show
The method
getBaseRequest() does not exist on Symfony\Component\BrowserKit\Client. Did you maybe mean request()?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise. Loading history...
|
|||
| 185 | 1 | } |
|
| 186 | |||
| 187 | 1 | if (isset($this->baseResponse)) { |
|
| 188 | 1 | $this->baseResponse = $this->client->getBaseResponse(); |
|
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
Symfony\Component\BrowserKit\Client as the method getBaseResponse() does only exist in the following sub-classes of Symfony\Component\BrowserKit\Client: Jasny\Codeception\Connector. 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
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
Loading history...
|
|||
| 189 | 1 | } |
|
| 190 | 1 | } |
|
| 191 | |||
| 192 | |||
| 193 | 3 | parent::_after($test); |
|
| 194 | 3 | } |
|
| 195 | |||
| 196 | |||
| 197 | /** |
||
| 198 | * Wrapper around `ob_start()` |
||
| 199 | * @codeCoverageIgnore |
||
| 200 | */ |
||
| 201 | protected function obStart() |
||
| 202 | { |
||
| 203 | ob_start(); |
||
| 204 | } |
||
| 205 | |||
| 206 | /** |
||
| 207 | * Wrapper around `ob_get_level()` |
||
| 208 | * @codeCoverageIgnore |
||
| 209 | * |
||
| 210 | * @return int |
||
| 211 | */ |
||
| 212 | protected function obGetLevel() |
||
| 213 | { |
||
| 214 | return ob_get_level(); |
||
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * Wrapper around `ob_clean()` |
||
| 219 | * @codeCoverageIgnore |
||
| 220 | */ |
||
| 221 | protected function obClean() |
||
| 222 | { |
||
| 223 | ob_clean(); |
||
| 224 | } |
||
| 225 | |||
| 226 | /** |
||
| 227 | * Wrapper around `session_status()` |
||
| 228 | * @codeCoverageIgnore |
||
| 229 | * |
||
| 230 | * @return int |
||
| 231 | */ |
||
| 232 | protected function sessionStatus() |
||
| 233 | { |
||
| 234 | return session_status(); |
||
| 235 | } |
||
| 236 | |||
| 237 | /** |
||
| 238 | * Wrapper around `session_abort()` |
||
| 239 | * @codeCoverageIgnore |
||
| 240 | */ |
||
| 241 | protected function sessionAbort() |
||
|
0 ignored issues
–
show
The return type could not be reliably inferred; please add a
@return annotation.
Our type inference engine in quite powerful, but sometimes the code does not
provide enough clues to go by. In these cases we request you to add a Loading history...
|
|||
| 242 | { |
||
| 243 | return session_abort(); |
||
| 244 | } |
||
| 245 | } |
||
| 246 |
Let’s take a look at an example:
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
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the parent class: