protected static function convertPHP(&$head, &$tail, $passthrough)
	{
		// Test whether the attributes must be saved when rendering the head because they're needed
		// when rendering the tail
		$saveAttributes = (bool) preg_match('(\\$node->(?:get|has)Attribute)', $tail);

		// Collect the names of all the attributes so that we can initialize them with a null value
		// to avoid undefined variable notices. We exclude attributes that seem to be in an if block
		// that tests its existence beforehand. This last part is not an accurate process as it
		// would be much more expensive to do it accurately but where it fails the only consequence
		// is we needlessly add the attribute to the list. There is no difference in functionality
		preg_match_all(
			"(\\\$node->getAttribute\\('([^']+)'\\))",
			preg_replace_callback(
				'(if\\(\\$node->hasAttribute\\(([^\\)]+)[^}]+)',
				function ($m)
				{
					return str_replace('$node->getAttribute(' . $m[1] . ')', '', $m[0]);
				},
				$head . $tail
			),
			$matches
		);
		$attrNames = array_unique($matches[1]);

		// Replace the source in $head and $tail
		self::replacePHP($head);
		self::replacePHP($tail);

		if (!$passthrough && strpos($head, '$node->textContent') !== false)
		{
			$head = '$textContent=$this->getQuickTextContent($xml);' . str_replace('$node->textContent', '$textContent', $head);
		}

		if (!empty($attrNames))
		{
			ksort($attrNames);
			$head = "\$attributes+=['" . implode("'=>null,'", $attrNames) . "'=>null];" . $head;
		}

		if ($saveAttributes)
		{
			$head .= '$this->attributes[]=$attributes;';
			$tail  = '$attributes=array_pop($this->attributes);' . $tail;
		}
	}