Completed
Pull Request — 1.2 (#12)
by David
05:26
created

ChainRenderer::getRendererDebugMessage()   B

Complexity

Conditions 11
Paths 15

Size

Total Lines 40

Duplication

Lines 18
Ratio 45 %

Importance

Changes 0
Metric Value
dl 18
loc 40
rs 7.3166
c 0
b 0
f 0
cc 11
nc 15
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * Copyright (c) 2013 David Negrier
4
 *
5
 * See the file LICENSE.txt for copying permission.
6
 */
7
8
namespace Mouf\Html\Renderer;
9
10
use Psr\Container\ContainerInterface;
11
use Psr\SimpleCache\CacheInterface;
12
13
/**
14
 * This class is a renderer that renders objects using other renderers.
15
 * This renderer will automatically detect the renderers to be included.
16
 * They must extend the ChainableRendererInterface interface.
17
 *
18
 * @author David Négrier <[email protected]>
19
 */
20
class ChainRenderer implements CanSetTemplateRendererInterface
21
{
22
23
    /**
24
     * @var ChainableRendererInterface|null
25
     */
26
    private $templateRenderer;
27
    /**
28
     * @var ChainableRendererInterface[]
29
     */
30
    private $packageRenderers = [];
31
    /**
32
     * @var ChainableRendererInterface[]
33
     */
34
    private $customRenderers = [];
35
36
    private $cacheService;
37
38
    private $initDone = false;
39
    /**
40
     * @var string[]
41
     */
42
    private $customRendererInstanceNames;
43
    /**
44
     * @var string
45
     */
46
    private $templateRendererInstanceName;
47
    /**
48
     * @var string[]
49
     */
50
    private $packageRendererInstanceNames;
51
    /**
52
     * @var string
53
     */
54
    private $uniqueName;
55
    /**
56
     * @var ContainerInterface
57
     */
58
    private $container;
59
60
    /**
61
     *
62
     * @param string[] $customRendererInstanceNames An array of names of custom renderers (container identifiers)
63
     * @param string[] $packageRendererInstanceNames An array of names of package renderers (container identifiers)
64
     * @param CacheInterface $cacheService This service is used to speed up the mapping between the object and the template.
65
     * @param string $uniqueName The unique name for this instance (used for caching purpose)
66
     */
67
    public function __construct(ContainerInterface $container, array $customRendererInstanceNames, array $packageRendererInstanceNames, CacheInterface $cacheService, string $uniqueName)
68
    {
69
        $this->container = $container;
70
        $this->customRendererInstanceNames = $customRendererInstanceNames;
71
        $this->packageRendererInstanceNames = $packageRendererInstanceNames;
72
        $this->cacheService = $cacheService;
73
        $this->uniqueName = $uniqueName;
74
    }
75
76
    /**
77
     * (non-PHPdoc)
78
     * @see \Mouf\Html\Renderer\RendererInterface::render()
79
     */
80
    public function render($object, string $context = null): void
81
    {
82
        $renderer = $this->getRenderer($object, $context);
83
        if ($renderer == null) {
84
            throw new NoRendererFoundException("Renderer not found. Unable to find renderer for object of class '".get_class($object)."'. Path tested: ".$this->getRendererDebugMessage($object, $context));
85
        }
86
        $renderer->render($object, $context);
87
    }
88
89
    /**
90
     * @param object $object
91
     * @param string|null $context
92
     * @return ChainableRendererInterface
93
     */
94
    private function getRenderer($object, string $context = null): ChainableRendererInterface
95
    {
96
        $cacheKey = "chainRendererByClass_".md5($this->uniqueName."/".$this->templateRendererInstanceName."/".get_class($object)."/".$context);
97
98
        $cachedInstanceName = $this->cacheService->get($cacheKey);
99
        if ($cachedInstanceName !== null) {
100
            return $this->container->get($cachedInstanceName);
101
        }
102
103
        $this->initRenderersList();
104
105
        $isCachable = true;
106
        $foundRenderer = null;
107
        $source = null;
0 ignored issues
show
Unused Code introduced by
$source is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
108
        $foundInstanceName = null;
109
110
        do {
111 View Code Duplication
            foreach ($this->customRenderers as $instanceName => $renderer) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
112
                $result = $renderer->canRender($object, $context);
113
                if ($result == ChainableRendererInterface::CAN_RENDER_OBJECT || $result == ChainableRendererInterface::CANNOT_RENDER_OBJECT) {
114
                    $isCachable = false;
115
                }
116
                if ($result == ChainableRendererInterface::CAN_RENDER_OBJECT || $result == ChainableRendererInterface::CAN_RENDER_CLASS) {
117
                    $foundRenderer = $renderer;
118
                    $foundInstanceName = $instanceName;
119
                    break 2;
120
                }
121
            }
122
123
            if ($this->templateRendererInstanceName && !$this->templateRenderer) {
124
                $this->templateRenderer = $this->container->get($this->templateRendererInstanceName);
125
            }
126
            if ($this->templateRenderer) {
127
                $result = $this->templateRenderer->canRender($object, $context);
128
                if ($result == ChainableRendererInterface::CAN_RENDER_OBJECT || $result == ChainableRendererInterface::CANNOT_RENDER_OBJECT) {
129
                    $isCachable = false;
130
                }
131
                if ($result == ChainableRendererInterface::CAN_RENDER_OBJECT || $result == ChainableRendererInterface::CAN_RENDER_CLASS) {
132
                    $foundRenderer = $this->templateRenderer;
133
                    break;
134
                }
135
            }
136
137 View Code Duplication
            foreach ($this->packageRenderers as $instanceName => $renderer) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
                $result = $renderer->canRender($object, $context);
139
                if ($result == ChainableRendererInterface::CAN_RENDER_OBJECT || $result == ChainableRendererInterface::CANNOT_RENDER_OBJECT) {
140
                    $isCachable = false;
141
                }
142
                if ($result == ChainableRendererInterface::CAN_RENDER_OBJECT || $result == ChainableRendererInterface::CAN_RENDER_CLASS) {
143
                    $foundRenderer = $renderer;
144
                    $foundInstanceName = $instanceName;
145
                    break 2;
146
                }
147
            }
148
        } while (false);
149
150
        if ($isCachable && $foundRenderer) {
151
            $this->cacheService->set($cacheKey, $foundInstanceName);
152
        }
153
154
        return $foundRenderer;
155
    }
156
157
    /**
158
     * Returns a string explaining the steps done to find the renderer.
159
     *
160
     * @param  object $object
161
     * @param  string $context
162
     * @return string
163
     */
164
    private function getRendererDebugMessage($object, string $context = null): string
165
    {
166
        $debugMessage = '';
167
168
        $this->initRenderersList();
169
170
        do {
171 View Code Duplication
            foreach ($this->customRenderers as $renderer) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
172
                /* @var $renderer ChainableRendererInterface */
173
174
                $debugMessage .= $renderer->debugCanRender($object, $context);
175
                $result = $renderer->canRender($object, $context);
176
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CAN_RENDER_CLASS) {
177
                    break 2;
178
                }
179
            }
180
181
            /* @var $renderer ChainableRendererInterface */
182
            if ($this->templateRenderer) {
183
                $debugMessage .= $this->templateRenderer->debugCanRender($object, $context);
184
                $result = $this->templateRenderer->canRender($object, $context);
185
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CAN_RENDER_CLASS) {
186
                    break;
187
                }
188
            }
189
190 View Code Duplication
            foreach ($this->packageRenderers as $renderer) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
                /* @var $renderer ChainableRendererInterface */
192
193
                $debugMessage .= $renderer->debugCanRender($object, $context);
194
                $result = $renderer->canRender($object, $context);
195
                if ($result === ChainableRendererInterface::CAN_RENDER_OBJECT || $result === ChainableRendererInterface::CAN_RENDER_CLASS) {
196
                    break 2;
197
                }
198
            }
199
200
        } while (false);
201
		
202
		return $debugMessage;
203
	}
204
	
205
	/**
206
	 * Initializes the renderers list (from cache if available)
207
	 */
208
	private function initRenderersList(): void {
209
        if (!$this->initDone) {
210
            foreach ($this->customRendererInstanceNames as $instanceName) {
211
                $this->customRenderers[$instanceName] = $this->container->get($instanceName);
212
            }
213
            foreach ($this->packageRendererInstanceNames as $instanceName) {
214
                $this->packageRenderers[$instanceName] = $this->container->get($instanceName);
215
            }
216
217
            // Note: We ignore template renderers on purpose.
218
            $this->initDone = true;
219
        }
220
	}
221
222
    /**
223
     * Sets the renderer associated to the template.
224
     * There should be only one if these renderers.
225
     * It is the role of the template to subscribe to this renderer.
226
     *
227
     * @param string $templateRendererInstanceName The name of the template renderer in the container
228
     */
229
    public function setTemplateRendererInstanceName(string $templateRendererInstanceName): void
230
    {
231
        $this->templateRendererInstanceName = $templateRendererInstanceName;
232
    }
233
}
234