Phtml.js

{
    "type": "file",
    "namespace": {
        "type": "namespace",
        "name": "Taeluf",
        "declaration": "namespace Taeluf;",
        "class": [
            {
                "type": "class",
                "docblock": {
                    "type": "docblock",
                    "description": "Makes DUMDocument... less terrible, but still not truly good"
                },
                "namespace": "Taeluf",
                "fqn": "Taeluf\\Phtml",
                "name": "Phtml",
                "extends": "\\DOMDocument",
                "declaration": "class Phtml extends \\DOMDocument",
                "properties": [
                    {
                        "type": "property",
                        "modifiers": [
                            "protected",
                            "string"
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "The source HTML + PHP code\n",
                            "attribute": [
                                {
                                    "type": "attribute",
                                    "name": "var",
                                    "description": "string"
                                }
                            ]
                        },
                        "name": "src",
                        "declaration": "protected string $src;"
                    },
                    {
                        "type": "property",
                        "modifiers": [
                            "protected",
                            "string"
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "The source code with all the PHP replaced by placeholders"
                        },
                        "name": "cleanSrc",
                        "declaration": "protected string $cleanSrc;"
                    },
                    {
                        "type": "property",
                        "modifiers": [
                            "protected",
                            "array"
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "[ 'phpplaceholder' => $phpCode, 'placeholder2' => $morePHP ]"
                        },
                        "name": "php",
                        "declaration": "protected array $php;"
                    },
                    {
                        "type": "property",
                        "modifiers": [
                            "protected"
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "A random string used when adding php code to a node's tag declaration. This string is later removed during output()"
                        },
                        "name": "phpAttrValue",
                        "declaration": "protected $phpAttrValue;"
                    }
                ],
                "comments": [
                    "\/**",
                    "* True if the source html had a '<html>' tag",
                    "* Except we're not implementing that???",
                    "* @var bool",
                    "*\/",
                    "protected bool $isHTMLDoc = false;"
                ],
                "methods": [
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "name": "html",
                                "declaration": "$html"
                            }
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "Create a DOMDocument, passing your HTML + PHP to __construct. \n\n",
                            "attribute": [
                                {
                                    "type": "attribute",
                                    "name": "param",
                                    "description": "mixed $html a block of HTML + PHP code. It does not have to have PHP. PHP will be handled gracefully."
                                },
                                {
                                    "type": "attribute",
                                    "name": "return",
                                    "description": "void"
                                }
                            ]
                        },
                        "modifiers": [
                            "public"
                        ],
                        "name": "__construct",
                        "body": "parent::__construct();\n$this->srcHTML = $html;\n$parser = new PHTML\\PHPParser($html);\n$enc = $parser->pieces();\n$this->php = $enc->php;\n$this->cleanSrc = $enc->html;\n$this->cleanSrc = $this->cleanHTML($this->cleanSrc);\n$hideXmlErrors=true;\nlibxml_use_internal_errors($hideXmlErrors);\n$this->registerNodeClass('DOMElement', '\\\\Taeluf\\\\PHTML\\\\Node');\n$this->registerNodeClass('DOMText', '\\\\Taeluf\\\\PHTML\\\\TextNode');\n\/\/ $this->registerNodeClass('DOMText', 'RBText');\n$html = '<root>'.$this->cleanSrc.'<\/root>';\n$this->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);\n$this->formatOutput = true;\nlibxml_use_internal_errors(false);",
                        "declaration": "public function __construct($html)"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "arg_types": [
                                    "string"
                                ],
                                "name": "string",
                                "declaration": "string $string"
                            }
                        ],
                        "modifiers": [
                            "public"
                        ],
                        "name": "placeholder",
                        "return_types": [
                            "string"
                        ],
                        "body": "$placeholder = PHTML\\PHPParser::getRandomAlpha();\n$placeholder = 'php'.$placeholder.'php';\n$this->php[$placeholder] = $string;\nreturn $placeholder;",
                        "declaration": "public function placeholder(string $string): string"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "arg_types": [
                                    "string"
                                ],
                                "name": "placeholder",
                                "declaration": "string $placeholder"
                            }
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "Get the code that's represented by the placeholder",
                            "attribute": [
                                {
                                    "type": "attribute",
                                    "name": "return",
                                    "description": "the stored code or null"
                                }
                            ]
                        },
                        "modifiers": [
                            "public"
                        ],
                        "name": "codeFromPlaceholder",
                        "return_types": [
                            "string"
                        ],
                        "body": "$code = $this->php[trim($placeholder)] ?? null;\nreturn $code;",
                        "declaration": "public function codeFromPlaceholder(string $placeholder): string"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "arg_types": [
                                    "string"
                                ],
                                "name": "enclosedPHP",
                                "declaration": "string $enclosedPHP"
                            }
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "Get a placeholder for the given block of code\nIntention is to parse a single '<?php \/\/piece of php code ?>' and not '<?php \/\/stuff ?><?php \/\/more stuff?>'\nWhen used as intended, will return a single 'word' that is the placeholder for the given code\n",
                            "attribute": [
                                {
                                    "type": "attribute",
                                    "name": "param",
                                    "description": "mixed $enclosedPHP an HTML + PHP string"
                                },
                                {
                                    "type": "attribute",
                                    "name": "return",
                                    "description": "string the parsed block of content where PHP code blocks are replaced by placeholders."
                                }
                            ]
                        },
                        "modifiers": [
                            "public"
                        ],
                        "name": "phpPlaceholder",
                        "return_types": [
                            "string"
                        ],
                        "body": "$parser = new PHTML\\PHPParser($enclosedPHP);\n$enc = $parser->pieces();\n\/\/ This block doesn't work because it's over-eager. A workaround to just add code, regardless of open\/close tags, would be good.\n\/\/ if (count($enc->php)==0){\n    \/\/ $code = \\PHTML\\PHPParser::getRandomAlpha();\n    \/\/ $enc->php = [\"php${code}php\"=>$enclosedPhp];\n    \/\/ $enc->html = $code;\n\/\/ }\n$this->php = array_merge($this->php,$enc->php);\nreturn $enc->html;",
                        "declaration": "public function phpPlaceholder(string $enclosedPHP): string"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "arg_types": [
                                    "string"
                                ],
                                "name": "codeWithPlaceholders",
                                "declaration": "string $codeWithPlaceholders"
                            }
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "Decode the given code by replacing PHP placeholders with the PHP code itself\n",
                            "attribute": [
                                {
                                    "type": "attribute",
                                    "name": "param",
                                    "description": "mixed $str"
                                },
                                {
                                    "type": "attribute",
                                    "name": "return",
                                    "description": "void"
                                }
                            ]
                        },
                        "modifiers": [
                            "public"
                        ],
                        "name": "fillWithPHP",
                        "return_types": [
                            "string"
                        ],
                        "body": "$decoded = str_replace(array_keys($this->php),$this->php,$codeWithPlaceholders);\nreturn $decoded;",
                        "declaration": "public function fillWithPHP(string $codeWithPlaceholders): string"
                    },
                    {
                        "type": "method",
                        "args": [],
                        "docblock": {
                            "type": "docblock",
                            "description": "See output()\n",
                            "attribute": [
                                {
                                    "type": "attribute",
                                    "name": "return",
                                    "description": "string"
                                }
                            ]
                        },
                        "modifiers": [
                            "public"
                        ],
                        "name": "__toString",
                        "body": "return $this->output();",
                        "declaration": "public function __toString()"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "name": "withPHP",
                                "value": "true",
                                "declaration": "$withPHP=true"
                            }
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "Return the decoded document as as tring. All PHP will be back in its place\n",
                            "attribute": [
                                {
                                    "type": "attribute",
                                    "name": "param",
                                    "description": "mixed $withPHP passing FALSE means placeholders will still be present & PHP code will not be"
                                },
                                {
                                    "type": "attribute",
                                    "name": "return",
                                    "description": "string the final document with PHP where it belongs"
                                }
                            ]
                        },
                        "modifiers": [
                            "public"
                        ],
                        "name": "output",
                        "body": "\/\/ echo \"\\n\".'-start output call-'.\"\\n\";\n$list = $this->childNodes[0]->childNodes;\n$hiddenTagsNodes = $this->xpath('\/\/*[@hideOwnTag]');\nforeach ($hiddenTagsNodes as $htn){\n    if ($htn->hideOwnTag==false||$htn->hideOwnTag=='false'){\n        unset($htn->hideOwnTag);\n        continue;\n    }\n    $parent = $htn->parentNode;\n    $childNodeList = $htn->children;\n    foreach ($childNodeList as $child){\n        $htn->removeChild($child);\n        $parent->insertBefore($child, $htn);\n    }\n    $parent->removeChild($htn);\n}\n$html = '';\nforeach ($list as $item){\n    $html .= $this->saveHTML($item);\n}\n\/** Run the php-code-replacer as long as there is a placeholder (while preventing infinite looping) *\/\n$html = $this->fill_php($html, $withPHP);\n$html = $this->restoreHtml($html);\nreturn $html;",
                        "declaration": "public function output($withPHP=true)"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "name": "html",
                                "declaration": "$html"
                            },
                            {
                                "type": "arg",
                                "name": "withPHP",
                                "value": "true",
                                "declaration": "$withPHP=true"
                            }
                        ],
                        "modifiers": [
                            "public"
                        ],
                        "name": "fill_php",
                        "body": "$maxIters = 25;\n$iters = 0;\nwhile ($iters++<$maxIters&&preg_match('\/php([a-zA-Z]{26})php\/', $html, $match)){\n    foreach ($this->php as $id=>$code){\n        if ($withPHP)$html = str_replace($id,$code,$html);\n        else $html = str_replace($id,'',$html);\n    }\n}\nif (($phpAttrVal=$this->phpAttrValue)!=null){\n    $html = str_replace(\"=\\\"$phpAttrVal\\\"\", '', $html);\n}\nreturn $html;",
                        "declaration": "public function fill_php($html, $withPHP=true)"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "name": "xpath",
                                "declaration": "$xpath"
                            },
                            {
                                "type": "arg",
                                "name": "refNode",
                                "value": "null",
                                "declaration": "$refNode=null"
                            }
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "get the results of an xpath query\n",
                            "attribute": [
                                {
                                    "type": "attribute",
                                    "name": "param",
                                    "description": "mixed $xpath the xpath query, such as: \/\/tagname[@attributename=\"value\"]\n                 If you use a refnode, prepend '.' at the beginning of your xpath query string"
                                },
                                {
                                    "type": "attribute",
                                    "name": "param",
                                    "description": "mixed $refNode a parent-node to search under"
                                },
                                {
                                    "type": "attribute",
                                    "name": "return",
                                    "description": "array the resulting DomNodeList is converted to an array & returned"
                                }
                            ]
                        },
                        "modifiers": [
                            "public"
                        ],
                        "name": "xpath",
                        "body": "$xp = new \\DOMXpath($this);\nif ($refNode==null)$list =  $xp->query($xpath);\nelse $list = $xp->query($xpath,$refNode);\n$arr = [];\nforeach ($list as $item){\n    $arr[] = $item;\n}\nreturn $arr;",
                        "declaration": "public function xpath($xpath,$refNode=null)"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "name": "node",
                                "declaration": "$node"
                            },
                            {
                                "type": "arg",
                                "name": "phpCode",
                                "declaration": "$phpCode"
                            }
                        ],
                        "docblock": {
                            "type": "docblock",
                            "description": "Set an attribute that will place PHP code inside the tag declartion of a node. \nBasically: `<node phpCodePlaceholder>`, which pHtml will later convert to `<node <?='some_stuff'?>>`. \nThis avoids problems caused by attributes requiring a `=\"\"`, which `DOMDocument` automatically places.\n",
                            "attribute": [
                                {
                                    "type": "attribute",
                                    "name": "param",
                                    "description": "$phpCode A block of php code with opening & closing tags like <?='some stuff'?>"
                                },
                                {
                                    "type": "attribute",
                                    "name": "return",
                                    "description": "\\Taeluf\\PHTML\\ValuelessAttribute"
                                }
                            ]
                        },
                        "modifiers": [
                            "public"
                        ],
                        "name": "addPhpToTag",
                        "body": "$this->phpAttrValue = $this->phpAttrValue  ??  PHTML\\PHPParser::getRandomAlpha();\n$placeholder = $this->phpPlaceholder($phpCode);\n$node->setAttribute($placeholder, $this->phpAttrValue);\nreturn $placeholder;",
                        "declaration": "public function addPhpToTag($node, $phpCode)"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "arg_types": [
                                    "\\DOMNode"
                                ],
                                "name": "node",
                                "declaration": "\\DOMNode $node"
                            },
                            {
                                "type": "arg",
                                "name": "phpCode",
                                "declaration": "$phpCode"
                            }
                        ],
                        "modifiers": [
                            "public"
                        ],
                        "name": "insertCodeBefore",
                        "body": "\/\/ $placeholder = $this->phpPlaceholder($phpCode);\n$placeholder = $this->placeholder($phpCode);\n$text = new \\DOMText($placeholder);\nreturn $node->parentNode->insertBefore($text, $node);",
                        "declaration": "public function insertCodeBefore(\\DOMNode $node, $phpCode)"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "arg_types": [
                                    "\\DOMNode"
                                ],
                                "name": "node",
                                "declaration": "\\DOMNode $node"
                            },
                            {
                                "type": "arg",
                                "name": "phpCode",
                                "declaration": "$phpCode"
                            }
                        ],
                        "modifiers": [
                            "public"
                        ],
                        "name": "insertCodeAfter",
                        "body": "$placeholder = $this->placeholder($phpCode);\n$text = new \\DOMText($placeholder);\nif ($node->nextSibling!==null)return $node->parentNode->insertBefore($text,$node->nextSibling);\nreturn $node->parentNode->insertBefore($text);",
                        "declaration": "public function insertCodeAfter(\\DOMNode $node, $phpCode)"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "name": "html",
                                "declaration": "$html"
                            }
                        ],
                        "modifiers": [
                            "public"
                        ],
                        "name": "cleanHTML",
                        "body": "\/\/ fix doctype\n$html = preg_replace('\/\\<\\!DOCTYPE(.*)\\>\/i', '<tlfphtml-doctype$1><\/tlfphtml-doctype>',$html);\n\/\/ fix <html> tag\n$html = preg_replace('\/<html([ >])\/','<tlfphtml-html$1',$html);\n$html = str_ireplace('<\/html>', '<\/tlfphtml-html>', $html);\n\/\/ fix <head> tag\n$html = preg_replace('\/<head([ >])\/', '<tlfphtml-head$1',$html);\n$html = str_ireplace('<\/head>', '<\/tlfphtml-head>',$html);\nreturn $html;",
                        "declaration": "public function cleanHTML($html)"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "name": "html",
                                "declaration": "$html"
                            }
                        ],
                        "modifiers": [
                            "public"
                        ],
                        "name": "restoreHtml",
                        "body": "$html = preg_replace('\/\\<tlfphtml\\-doctype(.*)\\>\\<\\\/tlfphtml\\-doctype\\>\/i', '<!DOCTYPE$1>',$html);\n\/\/ fix <html> tag\n$html = str_ireplace('<tlfphtml-html','<html',$html);\n$html = str_ireplace('<\/tlfphtml-html>', '<\/html>', $html);\n\/\/ fix <head> tag\n$html = str_ireplace('<tlfphtml-head', '<head', $html);\n$html = str_ireplace('<\/tlfphtml-head>', '<\/head>', $html);\nreturn $html;",
                        "declaration": "public function restoreHtml($html)"
                    },
                    {
                        "type": "method",
                        "args": [
                            {
                                "type": "arg",
                                "name": "param",
                                "declaration": "$param"
                            }
                        ],
                        "modifiers": [
                            "public"
                        ],
                        "name": "__get",
                        "body": "if ($param == 'form'){\n    return $this->xpath('\/\/form')[0] ?? null;\n}",
                        "declaration": "public function __get($param)"
                    }
                ]
            }
        ]
    }
}