Completed
Branch Scrutinizer (3da711)
by Josh
03:32
created

BundleGenerator::exportCallback()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 26
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 26
ccs 10
cts 10
cp 1
rs 9.2222
c 0
b 0
f 0
cc 6
nc 10
nop 3
crap 6
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2019 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 callback 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 20
	public function __construct(Configurator $configurator)
36
	{
37 20
		$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 9
	public function generate($className, array $options = [])
52
	{
53
		// Add default options
54 9
		$options += ['autoInclude' => true];
55
56
		// Get the parser and renderer
57 9
		$objects  = $this->configurator->finalize();
58 9
		$parser   = $objects['parser'];
59 9
		$renderer = $objects['renderer'];
60
61
		// Split the bundle's class name and its namespace
62 9
		$namespace = '';
63 9
		if (preg_match('#(.*)\\\\([^\\\\]+)$#', $className, $m))
64
		{
65 1
			$namespace = $m[1];
66 1
			$className = $m[2];
67
		}
68
69
		// Start with the standard header
70 9
		$php = [];
71 9
		$php[] = '/**';
72 9
		$php[] = '* @package   s9e\TextFormatter';
73 9
		$php[] = '* @copyright Copyright (c) 2010-2019 The s9e Authors';
74 9
		$php[] = '* @license   http://www.opensource.org/licenses/mit-license.php The MIT License';
75 9
		$php[] = '*/';
76
77 9
		if ($namespace)
78
		{
79 1
			$php[] = 'namespace ' . $namespace . ';';
80 1
			$php[] = '';
81
		}
82
83
		// Generate and append the bundle class
84 9
		$php[] = 'abstract class ' . $className . ' extends \\s9e\\TextFormatter\\Bundle';
85 9
		$php[] = '{';
86 9
		$php[] = '	/**';
87 9
		$php[] = '	* @var s9e\\TextFormatter\\Parser Singleton instance used by parse()';
88 9
		$php[] = '	*/';
89 9
		$php[] = '	protected static $parser;';
90 9
		$php[] = '';
91 9
		$php[] = '	/**';
92 9
		$php[] = '	* @var s9e\\TextFormatter\\Renderer Singleton instance used by render()';
93 9
		$php[] = '	*/';
94 9
		$php[] = '	protected static $renderer;';
95 9
		$php[] = '';
96
97
		// Add the event callbacks if applicable
98
		$events = [
99
			'beforeParse'
100 9
				=> 'Callback executed before parse(), receives the original text as argument',
101
			'afterParse'
102
				=> 'Callback executed after parse(), receives the parsed text as argument',
103
			'beforeRender'
104
				=> 'Callback executed before render(), receives the parsed text as argument',
105
			'afterRender'
106
				=> 'Callback executed after render(), receives the output as argument',
107
			'beforeUnparse'
108
				=> 'Callback executed before unparse(), receives the parsed text as argument',
109
			'afterUnparse'
110
				=> 'Callback executed after unparse(), receives the original text as argument'
111
		];
112 9
		foreach ($events as $eventName => $eventDesc)
113
		{
114 9
			if (isset($options[$eventName]))
115
			{
116 1
				$php[] = '	/**';
117 1
				$php[] = '	* @var ' . $eventDesc;
118 1
				$php[] = '	*/';
119 1
				$php[] = '	public static $' . $eventName . ' = ' . var_export($options[$eventName], true) . ';';
120 1
				$php[] = '';
121
			}
122
		}
123
124 9
		$php[] = '	/**';
125 9
		$php[] = '	* Return a new instance of s9e\\TextFormatter\\Parser';
126 9
		$php[] = '	*';
127 9
		$php[] = '	* @return s9e\\TextFormatter\\Parser';
128 9
		$php[] = '	*/';
129 9
		$php[] = '	public static function getParser()';
130 9
		$php[] = '	{';
131
132 9
		if (isset($options['parserSetup']))
133
		{
134 1
			$php[] = '		$parser = ' . $this->exportObject($parser) . ';';
135 1
			$php[] = '		' . $this->exportCallback($namespace, $options['parserSetup'], '$parser') . ';';
136 1
			$php[] = '';
137 1
			$php[] = '		return $parser;';
138
		}
139
		else
140
		{
141 8
			$php[] = '		return ' . $this->exportObject($parser) . ';';
142
		}
143
144 9
		$php[] = '	}';
145 9
		$php[] = '';
146 9
		$php[] = '	/**';
147 9
		$php[] = '	* Return a new instance of s9e\\TextFormatter\\Renderer';
148 9
		$php[] = '	*';
149 9
		$php[] = '	* @return s9e\\TextFormatter\\Renderer';
150 9
		$php[] = '	*/';
151 9
		$php[] = '	public static function getRenderer()';
152 9
		$php[] = '	{';
153
154
		// If this is a PHP renderer and we know where it's saved, automatically load it as needed
155 9
		if (!empty($options['autoInclude'])
156 9
		 && $this->configurator->rendering->engine instanceof PHP
157 9
		 && isset($this->configurator->rendering->engine->lastFilepath))
158
		{
159 1
			$className = get_class($renderer);
160 1
			$filepath  = realpath($this->configurator->rendering->engine->lastFilepath);
161
162 1
			$php[] = '		if (!class_exists(' . var_export($className, true) . ', false)';
163 1
			$php[] = '		 && file_exists(' . var_export($filepath, true) . '))';
164 1
			$php[] = '		{';
165 1
			$php[] = '			include ' . var_export($filepath, true) . ';';
166 1
			$php[] = '		}';
167 1
			$php[] = '';
168
		}
169
170 9
		if (isset($options['rendererSetup']))
171
		{
172 1
			$php[] = '		$renderer = ' . $this->exportObject($renderer) . ';';
173 1
			$php[] = '		' . $this->exportCallback($namespace, $options['rendererSetup'], '$renderer') . ';';
174 1
			$php[] = '';
175 1
			$php[] = '		return $renderer;';
176
		}
177
		else
178
		{
179 8
			$php[] = '		return ' . $this->exportObject($renderer) . ';';
180
		}
181
182 9
		$php[] = '	}';
183 9
		$php[] = '}';
184
185 9
		return implode("\n", $php);
186
	}
187
188
	/**
189
	* Export a given callback as PHP code
190
	*
191
	* @param  string   $namespace Namespace in which the callback is execute
192
	* @param  callable $callback  Original callback
193
	* @param  string   $argument  Callback's argument (as PHP code)
194
	* @return string              PHP code
195
	*/
196 11
	protected function exportCallback($namespace, callable $callback, $argument)
197
	{
198 11
		if (is_array($callback) && is_string($callback[0]))
199
		{
200
			// Replace ['foo', 'bar'] with 'foo::bar'
201 1
			$callback = $callback[0] . '::' . $callback[1];
202
		}
203
204 11
		if (!is_string($callback))
205
		{
206 1
			return 'call_user_func(' . var_export($callback, true) . ', ' . $argument . ')';
207
		}
208
209
		// Ensure that the callback starts with a \
210 10
		if ($callback[0] !== '\\')
211
		{
212 5
			$callback = '\\' . $callback;
213
		}
214
215
		// Replace \foo\bar::baz() with bar::baz() if we're in namespace foo
216 10
		if (substr($callback, 0, 2 + strlen($namespace)) === '\\' . $namespace . '\\')
217
		{
218 4
			$callback = substr($callback, 2 + strlen($namespace));
219
		}
220
221 10
		return $callback . '(' . $argument . ')';
222
	}
223
224
	/**
225
	* Serialize and export a given object as PHP code
226
	*
227
	* @param  object $obj Original object
228
	* @return string      PHP code
229
	*/
230 11
	protected function exportObject($obj)
231
	{
232
		// Serialize the object
233 11
		$str = call_user_func($this->serializer, $obj);
234
235
		// Export the object's source
236 11
		$str = var_export($str, true);
237
238 11
		return $this->unserializer . '(' . $str . ')';
239
	}
240
}