Completed
Pull Request — master (#3)
by mw
01:47
created

ServicesManager::newManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Onoi\CallbackContainer;
4
5
use RuntimeException;
6
7
/**
8
 * Convenience class to handle services isolated from an active CallbackInstantiator
9
 * instance.
10
 *
11
 * @license GNU GPL v2+
12
 * @since 1.2
13
 *
14
 * @author mwjames
15
 */
16
class ServicesManager {
17
18
	/**
19
	 * @var CallbackInstantiator
20
	 */
21
	private $callbackInstantiator;
22
23
	/**
24
	 * @since 1.2
25
	 *
26
	 * @param CallbackInstantiator $callbackInstantiator
27
	 */
28 7
	public function __construct( CallbackInstantiator $callbackInstantiator ) {
29 7
		$this->callbackInstantiator = $callbackInstantiator;
30 7
	}
31
32
	/**
33
	 * @since 1.2
34
	 *
35
	 * @return ServicesManager
36
	 */
37 7
	public static function newManager() {
38 7
		return new self( new DeferredCallbackLoader() );
39
	}
40
41
	/**
42
	 * @since 1.2
43
	 *
44
	 * @param string $serviceName
45
	 * @param mixed $service
46
	 * @param string|null $type
47
	 */
48 5
	public function add( $serviceName, $service, $type = null ) {
49
50 5
		if ( !is_callable( $service ) ) {
51 5
			$service = function() use( $service ) {
52 2
				return $service;
53 5
			};
54 5
		}
55
56 5
		$this->callbackInstantiator->registerCallback( $serviceName, $service );
0 ignored issues
show
Documentation introduced by
$service is of type callable, but the function expects a object<Closure>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
57
58 5
		if ( $type !== null ) {
59 1
			$this->callbackInstantiator->registerExpectedReturnType( $serviceName, $type );
60 1
		}
61 5
	}
62
63
	/**
64
	 * @since 1.2
65
	 *
66
	 * @param string $serviceName
67
	 *
68
	 * @return boolean
69
	 */
70 5
	public function has( $serviceName ) {
71 5
		return $this->callbackInstantiator->isRegistered( $serviceName );
72
	}
73
74
	/**
75
	 * @since 1.2
76
	 *
77
	 * @param string $serviceName
78
	 *
79
	 * @return mixed
80
	 */
81 5
	public function getBy( $serviceName ) {
82
83 5
		if ( !$this->callbackInstantiator->isRegistered( $serviceName ) ) {
84 1
			throw new RuntimeException( "$serviceName is an unknown service." );
85
		}
86
87 4
		return $this->callbackInstantiator->singleton( $serviceName );
88
	}
89
90
	/**
91
	 * @since 1.2
92
	 *
93
	 * @param string $serviceName
94
	 */
95 1
	public function removeBy( $serviceName ) {
96 1
		$this->callbackInstantiator->deregister( $serviceName );
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Onoi\CallbackContainer\CallbackInstantiator as the method deregister() does only exist in the following implementations of said interface: Onoi\CallbackContainer\DeferredCallbackLoader.

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...
97 1
	}
98
99
	/**
100
	 * @since 1.2
101
	 *
102
	 * @param string $serviceName
103
	 * @param mixed $service
104
	 */
105 2
	public function overrideWith( $serviceName, $service ) {
106 2
		$this->callbackInstantiator->registerObject( $serviceName, $service );
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Onoi\CallbackContainer\CallbackInstantiator as the method registerObject() does only exist in the following implementations of said interface: Onoi\CallbackContainer\DeferredCallbackLoader.

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...
107 2
	}
108
109
}
110