FromArrayBuilder   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 92
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 96.97%

Importance

Changes 0
Metric Value
dl 0
loc 92
ccs 32
cts 33
cp 0.9697
rs 10
c 0
b 0
f 0
wmc 9
lcom 1
cbo 3

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getInstance() 0 8 2
A setInstance() 0 4 1
B build() 0 35 4
A isBuilder() 0 6 1
1
<?php
2
namespace Malezha\Menu\Support;
3
4
use Illuminate\Contracts\Config\Repository;
5
use Illuminate\Contracts\Container\Container;
6
use Malezha\Menu\Contracts\Attributes;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Malezha\Menu\Support\Attributes.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
7
use Malezha\Menu\Contracts\Builder;
8
use Malezha\Menu\Contracts\ElementFactory;
9
use Malezha\Menu\Contracts\FromArrayBuilder as FromArrayBuilderContract;
10
use Malezha\Menu\Contracts\HasBuilder;
11
12
class FromArrayBuilder implements FromArrayBuilderContract
13
{
14
    /**
15
     * @var static
16
     */
17
    protected static $instance = null;
18
19
    /**
20
     * @var Container
21
     */
22
    private $container;
23
24
    /**
25
     * FromArrayBuilder constructor.
26
     *
27
     * @param Container $container
28
     */
29 53
    public function __construct(Container $container)
30
    {
31 53
        $this->container = $container;
32 53
    }
33
34
    /**
35
     * @inheritdoc
36
     */
37 2
    public static function getInstance()
38
    {
39 2
        if (is_null(static::$instance)) {
40
            throw new \RuntimeException(static::class . ' must be set instance.');
41
        }
42
43 2
        return static::$instance;
44
    }
45
46
    /**
47
     * @param @inheritdoc
48
     */
49 53
    public static function setInstance($instance)
50
    {
51 53
        static::$instance = $instance;
0 ignored issues
show
Documentation Bug introduced by
$instance is of type object<Malezha\Menu\Contracts\FromArrayBuilder>, but the property $instance was declared to be of type object<Malezha\Menu\Support\FromArrayBuilder>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
52 53
    }
53
54
    /**
55
     * @param @inheritdoc
56
     */
57 2
    public function build(array $array)
58
    {
59 2
        $aliases = $this->container->make(Repository::class)->get('menu.alias');
60
61
        /** @var Builder $builder */
62 2
        $builder = $this->container->makeWith(Builder::class, [
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Container\Container as the method makeWith() does only exist in the following implementations of said interface: Illuminate\Container\Container.

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...
63 2
            'attributes' => $this->container->makeWith(Attributes::class,
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Container\Container as the method makeWith() does only exist in the following implementations of said interface: Illuminate\Container\Container.

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...
64 2
                ['attributes' => $array['attributes']]),
65 2
            'activeAttributes' => $this->container->makeWith(Attributes::class,
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Container\Container as the method makeWith() does only exist in the following implementations of said interface: Illuminate\Container\Container.

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...
66 2
                ['attributes' => $array['activeAttributes']]),
67 2
            'view' => $array['view'],
68 2
            'type' => $array['type'],
69
        ]);
70
71 2
        foreach ($array['elements'] as $key => $element) {
72 2
            $class = $aliases[$element['type']];
73
74 2
            $builder->create($key, $class, function (ElementFactory $factory) use ($class, $element) {
75
                // If element is submenu
76 2
                if ($this->isBuilder($class)) {
77 2
                    $element['builder'] = $this->build($element['builder']);
78
                }
79
80 2
                $attributes = preg_grep("/.*(attributes)/i", array_keys($element));
81
82 2
                foreach ($attributes as $key) {
83 2
                    $element[$key] = $this->container->makeWith(Attributes::class, ['attributes' => $element[$key]]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Container\Container as the method makeWith() does only exist in the following implementations of said interface: Illuminate\Container\Container.

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...
84
                }
85
86 2
                return $factory->build($element);
87 2
            });
88
        }
89
90 2
        return $builder;
91
    }
92
93
    /**
94
     * @param string $class
95
     * @return bool
96
     */
97 2
    private function isBuilder($class)
98
    {
99 2
        $reflection = new \ReflectionClass($class);
100
101 2
        return $reflection->implementsInterface(HasBuilder::class);
102
    }
103
}