Passed
Pull Request — main (#22)
by Andrey
28:59 queued 16:23
created

Instance::of()   B

Complexity

Conditions 9
Paths 5

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 14
c 1
b 0
f 0
nc 5
nop 2
dl 0
loc 25
rs 8.0555
1
<?php
2
3
namespace Helldar\Support\Helpers;
4
5
use Helldar\Support\Facades\Helpers\Arr;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Helldar\Support\Helpers\Arr. Consider defining an alias.

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...
6
use Helldar\Support\Facades\Helpers\Is;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Helldar\Support\Helpers\Is. Consider defining an alias.

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 Helldar\Support\Facades\Helpers\Reflection;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Helldar\Support\Helpers\Reflection. Consider defining an alias.

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...
8
use ReflectionClass;
9
10
final class Instance
11
{
12
    /**
13
     * Checks if the item being checked inherits from other objects and interfaces.
14
     *
15
     * @param  object|string  $haystack
16
     * @param  string|string[]  $needles
17
     *
18
     * @return bool
19
     */
20
    public function of($haystack, $needles): bool
21
    {
22
        if (! $this->exists($haystack)) {
23
            return false;
24
        }
25
26
        $reflection = $this->resolve($haystack);
0 ignored issues
show
Bug introduced by
It seems like $haystack can also be of type string; however, parameter $class of Helldar\Support\Helpers\Instance::resolve() does only seem to accept ReflectionClass|object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

26
        $reflection = $this->resolve(/** @scrutinizer ignore-type */ $haystack);
Loading history...
27
        $classname  = $this->classname($haystack);
28
29
        foreach (Arr::wrap($needles) as $needle) {
30
            if (! $this->exists($needle)) {
31
                continue;
32
            }
33
34
            if (
35
                $haystack instanceof $needle ||
36
                $classname === $this->classname($needle) ||
37
                $reflection->isSubclassOf($needle) ||
38
                ($reflection->isInterface() && $reflection->implementsInterface($needle))
39
            ) {
40
                return true;
41
            }
42
        }
43
44
        return false;
45
    }
46
47
    /**
48
     * Extract the trailing name component from a file path.
49
     *
50
     * @param  object|string  $class
51
     *
52
     * @return string|null
53
     */
54
    public function basename($class): ?string
55
    {
56
        $class = $this->classname($class);
57
58
        return basename(str_replace('\\', '/', $class)) ?: null;
59
    }
60
61
    /**
62
     * Gets the class name of the object.
63
     *
64
     * @param  object|string|null  $class
65
     *
66
     * @return string|null
67
     */
68
    public function classname($class = null): ?string
69
    {
70
        if (Is::object($class)) {
71
            return get_class($class);
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type string; however, parameter $object of get_class() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

71
            return get_class(/** @scrutinizer ignore-type */ $class);
Loading history...
72
        }
73
74
        return class_exists($class) || interface_exists($class) ? $class : null;
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type object; however, parameter $class_name of class_exists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

74
        return class_exists(/** @scrutinizer ignore-type */ $class) || interface_exists($class) ? $class : null;
Loading history...
Bug introduced by
It seems like $class can also be of type object; however, parameter $interface_name of interface_exists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

74
        return class_exists($class) || interface_exists(/** @scrutinizer ignore-type */ $class) ? $class : null;
Loading history...
Bug Best Practice introduced by
The expression return class_exists($cla...$class) ? $class : null could return the type object which is incompatible with the type-hinted return null|string. Consider adding an additional type-check to rule them out.
Loading history...
75
    }
76
77
    /**
78
     * Checks if the object exists.
79
     *
80
     * @param  object|string  $haystack
81
     *
82
     * @return bool
83
     */
84
    public function exists($haystack): bool
85
    {
86
        if (Is::object($haystack)) {
87
            return true;
88
        }
89
90
        if (Is::string($haystack)) {
91
            return class_exists($haystack) || interface_exists($haystack);
0 ignored issues
show
Bug introduced by
It seems like $haystack can also be of type object; however, parameter $class_name of class_exists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

91
            return class_exists(/** @scrutinizer ignore-type */ $haystack) || interface_exists($haystack);
Loading history...
Bug introduced by
It seems like $haystack can also be of type object; however, parameter $interface_name of interface_exists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

91
            return class_exists($haystack) || interface_exists(/** @scrutinizer ignore-type */ $haystack);
Loading history...
92
        }
93
94
        return false;
95
    }
96
97
    /**
98
     * Calls a method on an object.
99
     *
100
     * @param  object  $object
101
     * @param  string  $method
102
     * @param  null  $default
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $default is correct as it would always require null to be passed?
Loading history...
103
     *
104
     * @return false|mixed|null
105
     */
106
    public function call($object, string $method, $default = null)
107
    {
108
        if (Is::object($object) && method_exists($object, $method)) {
109
            return call_user_func([$object, $method]);
110
        }
111
112
        return $default;
113
    }
114
115
    /**
116
     * Calls the object's methods one by one and returns the first non-empty value.
117
     *
118
     * @param  object  $object
119
     * @param  string|string[]  $methods
120
     * @param  null  $default
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $default is correct as it would always require null to be passed?
Loading history...
121
     *
122
     * @return false|mixed|null
123
     */
124
    public function callWhen($object, $methods, $default = null)
125
    {
126
        foreach (Arr::wrap($methods) as $method) {
127
            if ($value = $this->call($object, $method)) {
128
                return $value;
129
            }
130
        }
131
132
        return $default;
133
    }
134
135
    /**
136
     * Calls a method of an object that matches a class.
137
     *
138
     * @param  array  $map
139
     * @param  object  $value
140
     * @param  null  $default
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $default is correct as it would always require null to be passed?
Loading history...
141
     *
142
     * @return false|mixed|null
143
     */
144
    public function callOf(array $map, $value, $default = null)
145
    {
146
        foreach ($map as $class => $method) {
147
            if (Is::object($value) && $this->of($value, $class)) {
148
                return $this->call($value, $method, $default);
149
            }
150
        }
151
152
        return $default;
153
    }
154
155
    /**
156
     * Creates a ReflectionClass object.
157
     *
158
     * @param  object|ReflectionClass  $class
159
     *
160
     * @return \ReflectionClass
161
     */
162
    protected function resolve($class): ReflectionClass
163
    {
164
        return Reflection::resolve($class);
165
    }
166
}
167