Completed
Branch master (159ff9)
by Josh
34:06
created

src/Configurator/BundleGenerator.php (2 issues)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2017 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
	public function __construct(Configurator $configurator)
36
	{
37
		$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
	public function generate($className, array $options = [])
52
	{
53
		// Add default options
54
		$options += ['autoInclude' => true];
55
56
		// Get the parser and renderer
57
		$objects  = $this->configurator->finalize($options);
58
		$parser   = $objects['parser'];
59
		$renderer = $objects['renderer'];
60
61
		// Split the bundle's class name and its namespace
62
		$namespace = '';
63
		if (preg_match('#(.*)\\\\([^\\\\]+)$#', $className, $m))
64
		{
65
			$namespace = $m[1];
66
			$className = $m[2];
67
		}
68
69
		// Start with the standard header
70
		$php = [];
71
		$php[] = '/**';
72
		$php[] = '* @package   s9e\TextFormatter';
73
		$php[] = '* @copyright Copyright (c) 2010-2017 The s9e Authors';
74
		$php[] = '* @license   http://www.opensource.org/licenses/mit-license.php The MIT License';
75
		$php[] = '*/';
76
77
		if ($namespace)
78
		{
79
			$php[] = 'namespace ' . $namespace . ';';
80
			$php[] = '';
81
		}
82
83
		// Generate and append the bundle class
84
		$php[] = 'abstract class ' . $className . ' extends \\s9e\\TextFormatter\\Bundle';
85
		$php[] = '{';
86
		$php[] = '	/**';
87
		$php[] = '	* @var s9e\\TextFormatter\\Parser Singleton instance used by parse()';
88
		$php[] = '	*/';
89
		$php[] = '	protected static $parser;';
90
		$php[] = '';
91
		$php[] = '	/**';
92
		$php[] = '	* @var s9e\\TextFormatter\\Renderer Singleton instance used by render()';
93
		$php[] = '	*/';
94
		$php[] = '	protected static $renderer;';
95
		$php[] = '';
96
97
		// Add the event callbacks if applicable
98
		$events = [
99
			'beforeParse'
100
				=> '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
		foreach ($events as $eventName => $eventDesc)
113
		{
114
			if (isset($options[$eventName]))
115
			{
116
				$php[] = '	/**';
117
				$php[] = '	* @var ' . $eventDesc;
118
				$php[] = '	*/';
119
				$php[] = '	public static $' . $eventName . ' = ' . var_export($options[$eventName], true) . ';';
120
				$php[] = '';
121
			}
122
		}
123
124
		$php[] = '	/**';
125
		$php[] = '	* Return a new instance of s9e\\TextFormatter\\Parser';
126
		$php[] = '	*';
127
		$php[] = '	* @return s9e\\TextFormatter\\Parser';
128
		$php[] = '	*/';
129
		$php[] = '	public static function getParser()';
130
		$php[] = '	{';
131
132
		if (isset($options['parserSetup']))
133
		{
134
			$php[] = '		$parser = ' . $this->exportObject($parser) . ';';
0 ignored issues
show
$parser is of type object<s9e\TextFormatter\Parser>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
135
			$php[] = '		' . $this->exportCallback($namespace, $options['parserSetup'], '$parser') . ';';
136
			$php[] = '';
137
			$php[] = '		return $parser;';
138
		}
139
		else
140
		{
141
			$php[] = '		return ' . $this->exportObject($parser) . ';';
0 ignored issues
show
$parser is of type object<s9e\TextFormatter\Parser>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
142
		}
143
144
		$php[] = '	}';
145
		$php[] = '';
146
		$php[] = '	/**';
147
		$php[] = '	* Return a new instance of s9e\\TextFormatter\\Renderer';
148
		$php[] = '	*';
149
		$php[] = '	* @return s9e\\TextFormatter\\Renderer';
150
		$php[] = '	*/';
151
		$php[] = '	public static function getRenderer()';
152
		$php[] = '	{';
153
154
		// If this is a PHP renderer and we know where it's saved, automatically load it as needed
155
		if (!empty($options['autoInclude'])
156
		 && $this->configurator->rendering->engine instanceof PHP
157
		 && isset($this->configurator->rendering->engine->lastFilepath))
158
		{
159
			$className = get_class($renderer);
160
			$filepath  = realpath($this->configurator->rendering->engine->lastFilepath);
161
162
			$php[] = '		if (!class_exists(' . var_export($className, true) . ', false)';
163
			$php[] = '		 && file_exists(' . var_export($filepath, true) . '))';
164
			$php[] = '		{';
165
			$php[] = '			include ' . var_export($filepath, true) . ';';
166
			$php[] = '		}';
167
			$php[] = '';
168
		}
169
170
		if (isset($options['rendererSetup']))
171
		{
172
			$php[] = '		$renderer = ' . $this->exportObject($renderer) . ';';
173
			$php[] = '		' . $this->exportCallback($namespace, $options['rendererSetup'], '$renderer') . ';';
174
			$php[] = '';
175
			$php[] = '		return $renderer;';
176
		}
177
		else
178
		{
179
			$php[] = '		return ' . $this->exportObject($renderer) . ';';
180
		}
181
182
		$php[] = '	}';
183
		$php[] = '}';
184
185
		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
	protected function exportCallback($namespace, callable $callback, $argument)
197
	{
198
		if (is_array($callback) && is_string($callback[0]))
199
		{
200
			// Replace ['foo', 'bar'] with 'foo::bar'
201
			$callback = $callback[0] . '::' . $callback[1];
202
		}
203
204
		if (!is_string($callback))
205
		{
206
			return 'call_user_func(' . var_export($callback, true) . ', ' . $argument . ')';
207
		}
208
209
		// Ensure that the callback starts with a \
210
		if ($callback[0] !== '\\')
211
		{
212
			$callback = '\\' . $callback;
213
		}
214
215
		// Replace \foo\bar::baz() with bar::baz() if we're in namespace foo
216
		if (substr($callback, 0, 2 + strlen($namespace)) === '\\' . $namespace . '\\')
217
		{
218
			$callback = substr($callback, 2 + strlen($namespace));
219
		}
220
221
		return $callback . '(' . $argument . ')';
222
	}
223
224
	/**
225
	* Serialize and export a given object as PHP code
226
	*
227
	* @param  string $obj Original object
228
	* @return string      PHP code
229
	*/
230
	protected function exportObject($obj)
231
	{
232
		// Serialize the object
233
		$str = call_user_func($this->serializer, $obj);
234
235
		// Export the object's source
236
		$str = var_export($str, true);
237
238
		return $this->unserializer . '(' . $str . ')';
239
	}
240
}