<?php
namespace Taeluf\PHTML;
/**
* Parses .php files into:
* 1. A string with placeholders for the PHP code
* 2. An array of PHP code, identified by their placeholders
*
*/
class PHPParser {
/**
* The source HTML + PHP code
*
* @var string
*/
protected string $src;
/**
* The parsed pieces of code in the format:
* [
* 'html' => '<div>A string of code with php placeholders like <b>phpadsfwefhaksldfjaskdlfjaskldfjasldfkphp</b> </div> and trailing text...',
* 'php' => [
* 'phpadsfwefhaksldfjaskdlfjaskldfjasldfkphp' => '<?="Something echod";?>',
* 'phpid2'=>'<?php // another code block/?>'
* ]
* ]
* The array is simply (object) cast
*/
protected object $pieces;
/**
* Create a new PHP parser instance from a string
*
* @param mixed $htmlPHPString - a block of HTML + PHP code. PHP code will be inside open (<?php or <?=) and close (?>) tags
* @return void
*/
public function __construct(string $htmlPHPString){
$this->src = $htmlPHPString;
$this->pieces = $this->separatePHP();
}
/**
* Return the parsed pieces. See doc for protected $pieces
*
* @return object
*/
public function pieces(): object{
return $this->pieces;
}
/** Separate the source code into it's pieces. See the protected $pieces docs
*
* @return object just an array cast to object
*/
protected function separatePHP(): object{
$tokens = $this->tokens();
$str = '';
$inPHP = false;
$phpCode = '';
$phpList = [];
foreach ($tokens as $index => $token){
$token = (object)$token;
if ($token->type=='T_OPEN_TAG'
||$token->type=='T_OPEN_TAG_WITH_ECHO'){
$inPHP = true;
}
if (!$inPHP){
$str .= $token->code;
}
if ($inPHP){
$phpCode .= $token->code;
}
if ($token->type=='T_CLOSE_TAG'){
$id = 'php'.$this->randomAlpha().'php';
$phpList[$id] = $phpCode;
$phpCode = '';
$str .= $id;
$inPHP = false;
}
}
return (object)[
'php'=>$phpList,
'html'=>$str
];
}
/**
* Generates a random string of characters a-z, all lowercase & returns them
* this is used for the PHP placeholders
*
* @param int $length
* @return string
*/
protected function randomAlpha($length = 26): string {
return static::getRandomAlpha($length);
}
/**
* Generates a random string of characters a-z, all lowercase & returns them
* this is used for the PHP placeholders
*
* @param int $length
* @return string
*/
static public function getRandomAlpha($length = 26): string {
$characters = 'abcdefghijklmnopqrstuvwxyz';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
/**
* Tokenize the src code into a slightly better format than token_get_all
*
* @return array of tokens
*/
protected function tokens(): array{
$content = $this->src;
$tokens = token_get_all($content);
$niceTokens = [];
$delLine = null;
foreach ($tokens as $index=>$token){
$tok = [];
if (!is_array($token)){
$lastTok = array_slice($niceTokens,-1)[0];
$tok['type'] = "UNNAMED_TOKEN";
$tok['code'] = $token;
$tok['line'] = $lastTok['line'];
} else {
$tok['type'] = token_name($token[0]);
$tok['code'] = $token[1];
$tok['line'] = $token[2];
}
// This is old code for an idea I had
// if ($tok['type']=='T_STRING'&&$tok['code']=='PHPPlus'){
// $next = $tokens[$index+1];
// if (is_array($next)&&$next[1]=='::'){
// $delLine = $tok['line'];
// echo 'del line is '.$delLine."\n\n";
// }
// }
$niceTokens[] = $tok;
}
foreach ($niceTokens as $index=>$token){
if ($token['line']===$delLine){
unset($niceTokens[$index]);
}
}
return $niceTokens;
}
}