Completed
Push — master ( eb5224...04979f )
by Peter
11:53
created

CoarseChecker   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 45
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Test Coverage

Coverage 83.33%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 6
c 1
b 0
f 0
lcom 0
cbo 0
dl 0
loc 45
ccs 10
cts 12
cp 0.8333
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
B mightHaveAnnotations() 0 32 6
1
<?php
2
3
/*
4
 * To change this license header, choose License Headers in Project Properties.
5
 * To change this template file, choose Tools | Templates
6
 * and open the template in the editor.
7
 */
8
9
namespace Maslosoft\Addendum\Helpers;
10
11
use ReflectionClass;
12
use ReflectionObject;
13
use Reflector;
14
15
/**
16
 * Coarse check for annotations.
17
 *
18
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
19
 */
20
class CoarseChecker
21
{
22
23
	/**
24
	 * Check if file contains annotations,
25
	 * by checking if it contains @[A-Z] regular expression.
26
	 *
27
	 * It does not ensure that file really contains anniotations.
28
	 *
29
	 * @param string|Reflector|object $entity
30
	 */
31 42
	public static function mightHaveAnnotations($entity)
32
	{
33 42
		if (is_object($entity))
34
		{
35 42
			if ($entity instanceof Reflector)
36
			{
37 42
				if ($entity instanceof ReflectionClass)
38
				{
39 33
					$file = $entity->getFileName();
40
				}
41
				else
42
				{
43 42
					$file = $entity->getDeclaringClass()->getFileName();
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Reflector as the method getDeclaringClass() does only exist in the following implementations of said interface: Maslosoft\Addendum\Refle...flectionAnnotatedMethod, Maslosoft\Addendum\Refle...ectionAnnotatedProperty, ReflectionMethod, ReflectionParameter, ReflectionProperty.

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...
44
				}
45
			}
46
			else
47
			{
48 42
				$file = (new ReflectionObject($entity))->getFileName();
49
			}
50
		}
51
		else
52
		{
53
			$file = $entity;
54
		}
55 42
		if (empty($file) || !is_string($file))
56
		{
57
			return false;
58
//			throw new \UnexpectedValueException(sprintf('Should provide file name, got: %s', gettype($file)));
59
		}
60 42
		$content = file_get_contents($file);
61 42
		return preg_match('~@[A-Z]~', $content);
62
	}
63
64
}
65