BundleGenerator::generate()   F
last analyzed

Complexity

Conditions 12
Paths 384

Size

Total Lines 145
Code Lines 96

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 83
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 96
dl 0
loc 145
ccs 83
cts 83
cp 1
rs 3.1539
c 0
b 0
f 0
cc 12
nc 384
nop 2
crap 12

How to fix   Long Method    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
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) The s9e authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Configurator;
9
10
use s9e\TextFormatter\Configurator;
11
use s9e\TextFormatter\Configurator\RendererGenerators\PHP;
12
13
class BundleGenerator
14
{
15
	/**
16
	* @var Configurator Configurator this instance belongs to
17
	*/
18
	protected $configurator;
19
20
	/**
21
	* @var callable Callback used to serialize the objects
22
	*/
23
	public $serializer = 'serialize';
24
25
	/**
26
	* @var string Callback used to unserialize the serialized objects (must be a string)
27
	*/
28
	public $unserializer = 'unserialize';
29
30
	/**
31
	* Constructor
32
	*
33
	* @param  Configurator $configurator Configurator
34
	*/
35 23
	public function __construct(Configurator $configurator)
36
	{
37 23
		$this->configurator = $configurator;
38
	}
39
40
	/**
41
	* Create and return the source of a bundle based on given Configurator instance
42
	*
43
	* Options:
44
	*
45
	*  - autoInclude: automatically load the source of the PHP renderer (default: true)
46
	*
47
	* @param  string $className Name of the bundle class
48
	* @param  array  $options   Associative array of optional settings
49
	* @return string            PHP source for the bundle
50
	*/
51 12
	public function generate($className, array $options = [])
52
	{
53
		// Add default options
54 12
		$options += ['autoInclude' => true];
55
56
		// Copy the PHP files header if applicable
57 12
		if ($this->configurator->rendering->engine instanceof PHP)
58
		{
59 2
			$this->configurator->rendering->engine->phpHeader = $this->configurator->phpHeader;
60
		}
61
62
		// Get the parser and renderer
63 12
		$objects  = $this->configurator->finalize();
64 12
		$parser   = $objects['parser'];
65 12
		$renderer = $objects['renderer'];
66
67
		// Split the bundle's class name and its namespace
68 12
		$namespace = '';
69 12
		if (preg_match('#(.*)\\\\([^\\\\]+)$#', $className, $m))
70
		{
71 1
			$namespace = $m[1];
72 1
			$className = $m[2];
73
		}
74
75
		// Start with the standard header
76 12
		$php   = [];
77 12
		$php[] = $this->configurator->phpHeader;
78
79 12
		if ($namespace)
80
		{
81 1
			$php[] = 'namespace ' . $namespace . ';';
82 1
			$php[] = '';
83
		}
84
85
		// Generate and append the bundle class
86 12
		$php[] = 'abstract class ' . $className . ' extends \\s9e\\TextFormatter\\Bundle';
87 12
		$php[] = '{';
88 12
		$php[] = '	/**';
89 12
		$php[] = '	* @var ?\\s9e\\TextFormatter\\Parser Singleton instance used by parse()';
90 12
		$php[] = '	*/';
91 12
		$php[] = '	protected static ?\\s9e\\TextFormatter\\Parser $parser;';
92 12
		$php[] = '';
93 12
		$php[] = '	/**';
94 12
		$php[] = '	* @var ?\\s9e\\TextFormatter\\Renderer Singleton instance used by render()';
95 12
		$php[] = '	*/';
96 12
		$php[] = '	protected static ?\\s9e\\TextFormatter\\Renderer $renderer;';
97 12
		$php[] = '';
98
99
		// Add the event callbacks if applicable
100
		$events = [
101
			'beforeParse'
102 12
				=> 'Callback executed before parse(), receives the original text as argument',
103
			'afterParse'
104
				=> 'Callback executed after parse(), receives the parsed text as argument',
105
			'beforeRender'
106
				=> 'Callback executed before render(), receives the parsed text as argument',
107
			'afterRender'
108
				=> 'Callback executed after render(), receives the output as argument',
109
			'beforeUnparse'
110
				=> 'Callback executed before unparse(), receives the parsed text as argument',
111
			'afterUnparse'
112
				=> 'Callback executed after unparse(), receives the original text as argument'
113
		];
114 12
		foreach ($events as $eventName => $eventDesc)
115
		{
116 12
			if (isset($options[$eventName]))
117
			{
118 1
				$php[] = '	/**';
119 1
				$php[] = '	* @var ' . $eventDesc;
120 1
				$php[] = '	*/';
121 1
				$php[] = '	public static $' . $eventName . ' = ' . var_export($options[$eventName], true) . ';';
122 1
				$php[] = '';
123
			}
124
		}
125
126 12
		if (isset($objects['js']))
127
		{
128 1
			$php[] = '	/**';
129 1
			$php[] = '	* {@inheritdoc}';
130 1
			$php[] = '	*/';
131 1
			$php[] = '	public static function getJS(): string';
132 1
			$php[] = '	{';
133 1
			$php[] = '		return ' . var_export($objects['js'], true) . ';';
134 1
			$php[] = '	}';
135 1
			$php[] = '';
136
		}
137
138 12
		$php[] = '	/**';
139 12
		$php[] = '	* {@inheritdoc}';
140 12
		$php[] = '	*/';
141 12
		$php[] = '	public static function getParser(): \\s9e\\TextFormatter\\Parser';
142 12
		$php[] = '	{';
143
144 12
		if (isset($options['parserSetup']))
145
		{
146 1
			$php[] = '		$parser = ' . $this->exportObject($parser) . ';';
147 1
			$php[] = '		' . $this->exportCallback($namespace, $options['parserSetup'], '$parser') . ';';
148 1
			$php[] = '';
149 1
			$php[] = '		return $parser;';
150
		}
151
		else
152
		{
153 11
			$php[] = '		return ' . $this->exportObject($parser) . ';';
154
		}
155
156 12
		$php[] = '	}';
157 12
		$php[] = '';
158 12
		$php[] = '	/**';
159 12
		$php[] = '	* {@inheritdoc}';
160 12
		$php[] = '	*/';
161 12
		$php[] = '	public static function getRenderer(): \\s9e\\TextFormatter\\Renderer';
162 12
		$php[] = '	{';
163
164
		// If this is a PHP renderer and we know where it's saved, automatically load it as needed
165 12
		if (!empty($options['autoInclude'])
166 12
		 && $this->configurator->rendering->engine instanceof PHP
167 12
		 && isset($this->configurator->rendering->engine->lastFilepath))
168
		{
169 1
			$className = get_class($renderer);
170 1
			$filepath  = realpath($this->configurator->rendering->engine->lastFilepath);
171
172 1
			$php[] = '		if (!class_exists(' . var_export($className, true) . ', false)';
173 1
			$php[] = '		 && file_exists(' . var_export($filepath, true) . '))';
174 1
			$php[] = '		{';
175 1
			$php[] = '			include ' . var_export($filepath, true) . ';';
176 1
			$php[] = '		}';
177 1
			$php[] = '';
178
		}
179
180 12
		if (isset($options['rendererSetup']))
181
		{
182 1
			$php[] = '		$renderer = ' . $this->exportObject($renderer) . ';';
183 1
			$php[] = '		' . $this->exportCallback($namespace, $options['rendererSetup'], '$renderer') . ';';
184 1
			$php[] = '';
185 1
			$php[] = '		return $renderer;';
186
		}
187
		else
188
		{
189 11
			$php[] = '		return ' . $this->exportObject($renderer) . ';';
190
		}
191
192 12
		$php[] = '	}';
193 12
		$php[] = '}';
194
195 12
		return implode("\n", $php);
196
	}
197
198
	/**
199
	* Export a given callback as PHP code
200
	*
201
	* @param  string   $namespace Namespace in which the callback is execute
202
	* @param  callable $callback  Original callback
203
	* @param  string   $argument  Callback's argument (as PHP code)
204
	* @return string              PHP code
205
	*/
206 11
	protected function exportCallback($namespace, callable $callback, $argument)
207
	{
208 11
		if (is_array($callback) && is_string($callback[0]))
209
		{
210
			// Replace ['foo', 'bar'] with 'foo::bar'
211 1
			$callback = $callback[0] . '::' . $callback[1];
212
		}
213
214 11
		if (!is_string($callback))
215
		{
216 1
			return 'call_user_func(' . var_export($callback, true) . ', ' . $argument . ')';
217
		}
218
219
		// Ensure that the callback starts with a \
220 10
		if ($callback[0] !== '\\')
221
		{
222 5
			$callback = '\\' . $callback;
223
		}
224
225
		// Replace \foo\bar::baz() with bar::baz() if we're in namespace foo
226 10
		if (substr($callback, 0, 2 + strlen($namespace)) === '\\' . $namespace . '\\')
227
		{
228 4
			$callback = substr($callback, 2 + strlen($namespace));
229
		}
230
231 10
		return $callback . '(' . $argument . ')';
232
	}
233
234
	/**
235
	* Serialize and export a given object as PHP code
236
	*
237
	* @param  object $obj Original object
238
	* @return string      PHP code
239
	*/
240 14
	protected function exportObject($obj)
241
	{
242
		// Serialize the object
243 14
		$str = call_user_func($this->serializer, $obj);
244
245
		// Export the object's source
246 14
		$str = var_export($str, true);
247
248 14
		return $this->unserializer . '(' . $str . ')';
249
	}
250
}