Passed
Pull Request — master (#79)
by Glynn
05:50
created

Component_Compiler   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 101
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 10
eloc 28
c 1
b 0
f 1
dl 0
loc 101
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A compile() 0 2 1
A get_annotation() 0 13 3
A get_component_path() 0 26 4
A extract_annotation_value() 0 4 1
A __construct() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * The base component class.
7
 *
8
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
11
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
12
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
14
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
16
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
 *
20
 * @author Glynn Quelch <[email protected]>
21
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
22
 * @package PinkCrab\Perique\View
23
 * @since 1.2.0
24
 */
25
26
namespace PinkCrab\Perique\Services\View\Component;
27
28
use PinkCrab\Perique\Services\View\View_Model;
29
use Doctrine\Common\Annotations\AnnotationReader;
0 ignored issues
show
Bug introduced by
The type Doctrine\Common\Annotations\AnnotationReader was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
use Doctrine\Common\Annotations\AnnotationRegistry;
0 ignored issues
show
Bug introduced by
The type Doctrine\Common\Annotations\AnnotationRegistry was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
use PinkCrab\Perique\Services\View\Component\Component;
32
33
class Component_Compiler {
34
35
	/**
36
	 * The base path for components.
37
	 *
38
	 * If blank will assume views/components
39
	 *
40
	 * @var string
41
	 */
42
	private $component_base_path;
43
44
	/**
45
	 * All component aliases..
46
	 *
47
	 * @var array<string, string>
48
	 */
49
	private $component_aliases = array();
50
51
	/** @param array<string, string> $component_aliases */
52
	public function __construct( string $component_base_path = '', array $component_aliases = array() ) {
53
		$this->component_base_path = $component_base_path;
54
		$this->component_aliases   = $component_aliases;
55
	}
56
57
	/**
58
	 * Compiles the component into a view model.
59
	 *
60
	 * @param Component $component
61
	 * @return View_Model
62
	 */
63
	public function compile( Component $component ): View_Model {
64
		return new View_Model( $this->get_component_path( $component ), $component->get_variables() );
65
	}
66
67
	/**
68
	 * Returns the path to the component.
69
	 *
70
	 * @param Component $component
71
	 * @return string
72
	 */
73
	private function get_component_path( Component $component ): string {
74
75
		// Check aliases.
76
		if ( isset( $this->component_aliases[ get_class( $component ) ] ) ) {
77
			return esc_html( $this->component_aliases[ get_class( $component ) ] );
78
		}
79
80
		$from_annotation = $this->get_annotation( 'view', $component );
81
		// If it does have a path defined, use that.
82
		if ( ! empty( $from_annotation ) ) {
83
			return \trailingslashit( $this->component_base_path ) . $from_annotation;
84
		}
85
86
		// If the component has a defined path
87
		if ( $component->template() ) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $component->template() targeting PinkCrab\Perique\Service...t\Component::template() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
88
			return \trailingslashit( $this->component_base_path ) . $component->template();
0 ignored issues
show
Bug introduced by
Are you sure $component->template() of type void can be used in concatenation? ( Ignorable by Annotation )

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

88
			return \trailingslashit( $this->component_base_path ) . /** @scrutinizer ignore-type */ $component->template();
Loading history...
Bug introduced by
Are you sure the usage of $component->template() targeting PinkCrab\Perique\Service...t\Component::template() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
89
		}
90
91
		// Get path based on class name.
92
		$reflect    = new \ReflectionClass( $component );
93
		$short_name = $reflect->getShortName();
94
		// Add space between capitals, make lowercase and replace underscores with dashes.
95
		$short_name = strtolower( preg_replace( '/(?<!^)[A-Z]/', '-$0', $short_name ) ?? '' );
96
		$short_name = str_replace( '_', '-', $short_name );
97
98
		return \trailingslashit( $this->component_base_path ) . $short_name;
99
	}
100
101
	/**
102
	 * Attempts to extract a defined Annotation from component class doc block.
103
	 *
104
	 * @param string $annotation
105
	 * @param Component $component
106
	 * @return string|null
107
	 */
108
	private function get_annotation( string $annotation, Component $component ): ?string {
109
		$reflect = new \ReflectionClass( $component );
110
		$comment = $reflect->getDocComment();
111
112
		// if no comment, return null.
113
		if ( empty( $comment ) ) {
114
			return null;
115
		}
116
117
		// If comment contains the annotation, return the value.
118
		return strpos( $comment, "@{$annotation}" ) !== false
119
			? $this->extract_annotation_value( $comment, $annotation )
120
			: null;
121
	}
122
123
	/**
124
	 * Extracts the value from the annotation.
125
	 *
126
	 * @param string $comment
127
	 * @param string $annotation
128
	 * @return string|null
129
	 */
130
	private function extract_annotation_value( string $comment, string $annotation ): ?string {
131
		$pattern = "/@{$annotation}\s+(.*)/";
132
		preg_match( $pattern, $comment, $matches );
133
		return $matches[1] ?? null;
134
	}
135
136
}
137