CompilerOld.php

<!-- DO NOT EDIT. This file generated from template by Code Scrawl https://tluf.me/php/code-scrawl/ -->  
<?php  
  
namespace Phad;  
  
/**  
 * This thing is ... kinda trash ... but i just wanna keep it around for historical purposes ... i don't think it will ever be useful again ... but it will be cool to look at a few years from now & see how bad my code was lol  
 * - Reed (feb 4, 2022)  
 *  
 */  
class Compiler {  
  
    protected string $source;  
    protected array $output = [  
        'view'=>null,  
        'routes'=>null,  
        // 'idk what else'=>null,  
    ];  
    protected array $routes = [  
      
    ];  
  
    public function __construct(string $source){  
        $this->source = $source;  
        $this->compile();  
    }  
  
    protected function getPropNodes($item){  
        if ($item->tagName=='form')return $item->ownerDocument->xpath('descendant::*[@name]',$item);  
        else return $item->ownerDocument->xpath('descendant::*[@prop]', $item);  
    }  
  
    protected function getAccessNodes($item){  
        return $item->ownerDocument->xpath('descendant::access',$item);  
    }  
  
    protected function getStatusNodes($item){  
        return $item->ownerDocument->xpath('descendant::on', $item);  
    }  
    protected function getErrorNodes($item){  
        return $item->ownerDocument->xpath('descendant::error', $item);  
    }  
  
    protected function indent(string $str, int $numSpaces, $padFirstLine = true){  
        $str = explode("\n", $str);  
        $pad = str_pad('',$numSpaces);  
        $str = implode("\n$pad",$str);  
        if ($padFirstLine)$str = $pad.$str;  
        return $str;  
    }  
  
    public function routes(){  
        return $this->routes;  
    }  
  
    public function compile(){  
        $source = $this->source;  
        $phtml = new \Taeluf\PHTML($source);  
  
  
        //build sitemap array from sitemap nodes  
        $sitemapNodeList = $phtml->xpath('//sitemap');  
        $sitemaps = [];  
        foreach ($sitemapNodeList as $sn){  
            $pattern = $sn->parentNode->pattern;  
            // $sitemap = ['pattern'=>$pattern];  
            $sitemal = [];  
            foreach ($sn->attributes() as $attr){  
                $sitemap[$attr->name] = $attr->value;  
            }  
            $sitemap['pattern']=$pattern;  
            $sitemaps[$pattern] = $sitemap;  
            $sn->parentNode->removeChild($sn);  
        }  
        // var_export sitemaps array into the compiled output & conditionally return the sitemap data  
  
        $code = "<?php if ((\$phad_mode??null)==='get_sitemap_data'):";  
        $code .= "\n    return ".var_export($sitemaps,true).';';  
        $code .= "\nendif; // close get_sitemap_data section ?>";  
  
  
        // $code = '<?php $this->sitemap = '.var_export($sitemaps,true).'; ';  
        // $code .= "\nif ((\$phad_mode??null)==='get_sitemap_data')return \$this->routes;";  
        $phtml->insertCodeBefore($phtml->childNodes[0]->childNodes[0]??null, $code);  
  
          
  
        // build routes array from route nodes  
        $routeNodeList = $phtml->xpath('//route');  
        $routes = [];  
        foreach ($routeNodeList as $rn){  
            $route = [];  
            foreach ($rn->attributes() as $attr){  
                $route[$attr->name] = $attr->value;  
            }  
            $routes[] = $route;  
            $rn->parentNode->removeChild($rn);  
        }  
        $this->routes = $routes;  
        // var_export routes array into the compiled output & conditionally return the routes  
        $code = '<?php $this->routes = '.var_export($routes,true).'; ';  
        $code .= "\nif ((\$phad_mode??null)==='get_routes')return \$this->routes;";  
        $code .= "\n?>";  
        $phtml->insertCodeBefore($phtml->childNodes[0]->childNodes[0]??null, $code);  
  
  
          
        //compile item nodes  
        $itemNodeList = $phtml->xpath('//*[@item]');  
        $limit = 50;  
        $iter = 0;  
        while ($iter++<$limit&&count($itemNodeList)>0){  
            foreach ($itemNodeList as $index=>$itemNode){  
                $children = $phtml->xpath('descendant::*[@item]', $itemNode);  
                //@bugfix(jan 18, 2022) form's with nested items were NOT getting their <onsubmit> included when using this strategy for handling child items, so I added the ->tagName!=form check. This means forms' nested items are handled differently & this MAY be problematic.  
                if (count($children) > 0  
                    && $itemNode->tagName!='form'  
                )continue;  
                unset($itemNodeList[$index]);  
                $this->compileItemNode($itemNode);  
            }  
        }  
  
        // compile individual nodes with access like `<a href="/whatever/" access="role:admin">...</a>`  
        $accessNodeList = $phtml->xpath('//*[@access]');  
        foreach ($accessNodeList as $accessNode){  
            $this->compileNodeWithAccess($accessNode);  
        }  
  
        $this->output['view'] = $phtml.'';  
    }  
  
    public function output(){  
        return $this->output;  
    }  
  
    protected function compileNodeWithAccess($node){  
  
        $node_info = $node->attributesAsArray();  
        $node_info['tagName'] = $node->tagName;  
        $node_info = var_export($node_info,true);  
        $node->doc->insertCodeBefore($node, "<?php if (\$phad->can_read_node($node_info)): ?>\n");  
        $node->doc->insertCodeAfter($node, "\n<?php endif; ?>");  
        unset($node->access);  
    }  
  
    protected function compileItemNode($itemNode){  
        // convenience vars  
        $phtml = $itemNode->ownerDocument;  
        $phpClose = '?>'; // stops my editor from being upset  
        $phpOpen = '<?php'; //just mildly convenient  
        $itemName = $itemNode->item;  
        $itemListVar = "\$${itemName}List";  
        $itemVar = "\$${itemName}";  
        $itemDataVar = "\$${itemName}Item";  
        $accessVar = "\$${itemName}Access";  
        $accessListVar = "\$${itemName}AccessList";  
        $code = [];  
  
        // special nodes  
        $propNodes = $this->getPropNodes($itemNode);  
        $accessNodes = $this->getAccessNodes($itemNode);  
        $statusNodes = $this->getStatusNodes($itemNode);  
        $errorNodes = $this->getErrorNodes($itemNode);  
  
        foreach ($errorNodes as $en){  
            $errorNodeCode = "<?=is_array(\$phad->failed_submit_columns) ? \n"  
                   ."    \$phad->validationErrorMessage(\$phad->failed_submit_columns) : ''?>";  
            $en->doc->insertCodeBefore($en, $errorNodeCode);  
        }  
  
        // itemdata code  
        $code[] = $this->getItemDataCode($itemDataVar, $itemName, $itemNode, $accessNodes);  
        $isForm = $itemNode->is('form');  
  
        // if ($isForm){  
        // }  
  
        // props code  
        $itemDataProperties = [];  
        $form_has_file_input = false;  
        foreach ($propNodes as $pn){  
            if ($isForm){  
                if ($pn->tagName=='input'&&$pn->type=='file'){  
                    $form_has_file_input = true;  
                }  
                $propsData = $pn->attributesAsArray();  
                unset($propsData['prop']);  
                unset($propsData['name']);  
                $propsData['tagName'] = $pn->tagName;  
                if ($pn->is('select')){  
                    foreach($pn->xpath('option') as $optNode){  
                        if (!$optNode->hasAttribute('value'))continue;  
                        $propsData['options'][] = $optNode->value;  
                    }  
                }  
  
                $itemDataProperties[$pn->prop ?? $pn->name] = $propsData;  
            }  
            $this->compilePropNode($itemNode, $pn);  
        }  
  
        if ($isForm){  
            $itemNode->action = $itemNode->action ?? '';  
            $itemNode->method = $itemNode->method ?? 'POST';  
            if ($form_has_file_input){  
                $itemNode->enctype = "multipart/form-data";  
            }  
  
            if (!isset($itemDataProperties['id'])){  
                $itemDataProperties['id'] = ['tagName'=>'input', 'type'=>'hidden'];  
            }  
            $itemDataPropertiesExported = var_export($itemDataProperties, true);  
            $code[] = $this->indent("${itemDataVar}->properties = $itemDataPropertiesExported;",4);  
        }  
        $code[] = <<<PHP  
            if (${itemDataVar}->phad_mode == 'get_item_data'){  
                return ${itemDataVar};  
            }  
            \$phad->resolveAccess($itemDataVar);   
        PHP;  
  
        if ($isForm&&isset($itemNode->deleteable)){  
            $code[] = <<<PHP  
                if (${itemDataVar}->phad_mode == 'delete'){  
                    \$phad->delete(${itemDataVar});  
                    return;  
                }  
            PHP;  
        }  
  
        $code[] =   
        "    \$phad->itemListStarted($itemDataVar);"  
            ;  
  
        $onSubmitForm = '';  
        if ($isForm){  
            $onSubmitForm = '//<onsubmit> code goes here';  
            $onSubmitNode = $itemNode->xpath('onsubmit')[0] ?? null;  
            if ($onSubmitNode!==null){  
                $onSubmitCode = $onSubmitNode->innerHTML;  
                $placeholder = $onSubmitNode->innerHTML;  
                $placeheldCode= $onSubmitNode->doc->codeFromPlaceholder($placeholder);  
                if ($placeheldCode!==null){  
                    $onSubmitCode = $placeheldCode;  
                }  
                $onSubmitForm = $phpClose.trim($onSubmitCode).$phpOpen;  
                $onSubmitForm = trim($this->indent($onSubmitForm, 8));  
            }  
        }  
        // display code (dealing with access index & status)  
        $submit_code = <<<PHP  
  
                    if ({$itemDataVar}->phad_mode == 'submit'){  
                        $onSubmitForm  
                        if ({$itemDataVar}->phad_mode=='submit'  
                            && \$phad->submit($itemDataVar, ${itemVar}Row) )return;  
                    }  
        PHP;  
        if ($itemNode->tagName!='form')$submit_code = '';  
        $foreachDisplayCode = <<<PHP  
  
                foreach(${itemDataVar}->list as ${itemVar}Row_Index=>${itemVar}Row):   
                    $itemVar = \$phad->objectFromRow($itemDataVar, ${itemVar}Row);  
                    if (!\$phad->hasRowAccess($itemDataVar, $itemVar))continue;  
                    {$submit_code}  
                    \$phad->rowStarted($itemDataVar, $itemVar);  
                    $phpClose  
  
        PHP;  
        $statusNodeData = $this->getStatusNodeData($statusNodes, $foreachDisplayCode, $phtml);  
  
        $this->cleanupNodes($itemNode, $propNodes, $accessNodes, $statusNodes);  
  
        $this->getDisplayLogicCode($beforeCode, $afterCode, $statusNodeData, $itemName, $phtml);  
        $code[] = $beforeCode;  
        $code = implode("\n", $code);  
        $phtml->insertCodeBefore($itemNode, $code);  
        $phtml->insertCodeAfter($itemNode, $afterCode);  
  
    }  
  
    protected function compilePropNode($item, $prop){  
        $phtml = $item->ownerDocument;  
        $itemName = $item->item;  
        $p = $prop;  
        $propName = $p->hasAttribute('prop') ? $p->prop : $p->name;  
        $propCode = '$'.$itemName.'->'.$propName;  
  
        $phpCode = null;  
        if ($p->hasAttribute('filter')){  
            $filterCode = var_export($p->filter,true);  
            $phpCode = '<?=$phad->filter('.$filterCode.','.$propCode.')?>';  
        } else {  
            $phpCode = '<?='.$propCode.'?>';  
        }  
  
        if ($p->tagName == 'input' && $p->type == 'backend'){  
  
        } elseif ($p->tagName=='input'){  
            $p->value = $phtml->phpPlaceholder($phpCode);  
        } else if ($p->tagName=='select'){  
            $options = $phtml->xpath('descendant::option', $p);  
            foreach ($options as $opt){  
                $optVal = var_export($opt->value,true);  
                $code = "<?=($optVal==$propCode)? ' selected=\"\" ' : ' '?>";  
                $phtml->addPhpToTag($opt, $code);  
            }  
        }   
        else {  
            $p->innerHTML=$phtml->phpPlaceholder($phpCode);  
        }  
  
        unset($p->filter);  
        unset($p->prop);  
    }  
  
    protected function cleanupNodes($itemNode, $propNodes, $accessNodes, $statusNodes){  
        unset($itemNode->item);  
        if ($itemNode->tagName=='x-item'){  
            $itemNode->hideOwnTag = true;  
        } else if ($itemNode->is('form')){  
            unset($itemNode->target);  
            unset($itemNode->deleteable);  
        }  
  
        // prop nodes are cleaned up earlier on... when prop nodes are compiled  
        // foreach ($propNodes as $p){  
            // unset($p->prop);  
        // }  
        foreach ($accessNodes as $a){  
            $a->parentNode->removeChild($a);  
        }   
        foreach ($statusNodes as $s){  
            $s->parentNode->removeChild($s);  
        }  
        foreach ($propNodes as $p){  
            if ($p->tagName=='input' && $p->type == 'backend'){  
                $p->parentNode->removeChild($p);  
            }  
        }  
  
        foreach ($itemNode->xpath('//onsubmit') as $node){  
            $node->parentNode->removeChild($node);  
        }  
  
        foreach ($itemNode->xpath('//error') as $errorNode){  
            $errorNode->parentNode->removeChild($errorNode);  
        }  
  
  
        foreach ($itemNode->xpath('//x-prop') as $xPropNode){  
            $xPropNode->hideOwnTag = true;  
        }  
    }  
  
    protected function getItemDataCode($itemDataVar, $itemName, $itemNode, $accessNodes){  
        $code = ['<?php '];  
        /** init the the item data var */  
  
        $formTarget = (function() use ($itemNode){  
            if (!$itemNode->is('form')){ //|| $itemNode->target==null){  
                // return ', "uhoh"=>false';  
                return ", 'item_type'=>'view'";  
                // return '';  
            }  
            $export = var_export($itemNode->target, true);  
            $targetCode = ", 'target'=> $export, 'item_type'=>'form'";  
            return $targetCode;  
        })();  
  
        $deleteable = '';  
        if (isset($itemNode->deleteable)&&$itemNode->is('form')){  
            $deleteable =   
                ", 'deleteable'=> "  
                    . var_export($itemNode->deleteable, true);   
        }  
  
        $code[] = $this->indent("$itemDataVar = (object)['accessList'=>[], 'args'=>\$args, 'list'=>[], 'name'=>'$itemName', 'accessStatus'=>false, 'phad_mode'=>\$phad_mode??'display'${formTarget}${deleteable}];",4);  
  
  
        /** [200 => [accessIndex => $codeToExecute]] */  
        $statusCode = [];  
        $accessIndex = -1;  
        foreach ($accessNodes as $a){  
            $accessIndex++;  
            $statusNodes = $a->ownerDocument->xpath('descendant::on',$a);  
            $accessArray = [];  
            foreach ($a->attributes() as $index=>$attr){  
                $accessArray[$attr->name] = $attr->value;  
            }  
            $accessExported = var_export($accessArray,true);  
            $accessExported = $this->indent($accessExported, 4, false);  
            $code[] = <<<PHP  
                ${itemDataVar}->accessList[] = $accessExported;  
            PHP;  
            // the status node needs this, later  
            $a->setAttribute('accessIndex', $accessIndex);  
        }  
          
        return implode("\n", $code);  
    }  
  
  
    protected function getStatusNodeData($statusNodes, $displayCode, $phtml){  
        // The target format:  
        // [  
            // 200 => [  
                // [2]=>$theActualCode, // 2 is the accessIndex  
                // ['else']=>$actualCode,  
                // ['then']=>$theForeachCode,  
            // ],  
//  
        // ];  
          
        $statusNodeData = [];  
        foreach ($statusNodes as $sn){  
            if (strtolower($sn->parentNode->tagName)=='access'){  
                $statusNodeData[$sn->getAttribute('s')][$sn->parentNode->accessIndex] = $phtml->placeholder($sn->innerHTML);  
            } else {  
                $statusNodeData[$sn->getAttribute('s')]['else'] = $phtml->placeholder($sn->innerHTML);  
            }  
        }  
        $statusNodeData[200]['then'] = $displayCode;  
  
        $snd = $statusNodeData;  
        foreach($snd as $status=>$list){  
            uasort($list, function($a, $b){  
                if (is_string($a))return -1;  
                else if (is_string($b))return 1;  
            });  
            $statusNodeData[$status] = $list;  
        }  
  
        return $statusNodeData;  
    }  
  
    public function getDisplayLogicCode(&$beforeCode, &$afterCode, $statusNodeData, $itemName, $phtml){  
  
        $itemDataVar = "\$${itemName}Item";  
        $itemVar = "\$${itemName}";  
  
        $closeTag = '?>';  
  
        $if = 'if';  
        $beforeCode = [];  
        $beforeCode[] = <<<PHP  
            if (${itemDataVar}->accessStatus == 200):  
        PHP;  
        $hasIf = false;  
        foreach ($statusNodeData[200] as $accessIndex=>$actualCode){  
            if (is_numeric($accessIndex)){  
                $hasIf = true;  
                $beforeCode[] = <<<PHP  
                        ${if} (${itemDataVar}->accessIndex == $accessIndex): $closeTag  
                            $actualCode  
                        <?php  
                PHP;  
                $if = 'elseif';  
            } else if ($accessIndex=='else'){  
                if ($hasIf){  
                    $beforeCode[] = <<<PHP  
                            else: $closeTag  
                                $actualCode  
                            <?php  
                    PHP;  
                } else {  
                    $beforeCode[] = <<<PHP  
                            if (true): $closeTag  
                            $actualCode  
                            <?php  
                    PHP;  
                }  
  
                $hasIf = true;  
            } else if ($accessIndex=='then'){  
                if ($hasIf){  
                    $beforeCode[] = $this->indent('endif; //close access status = 200 & access index = 0', 8);  
                }  
                $beforeCode[] = $actualCode;  
                continue;  
            }   
        }  
  
        $beforeCode = implode("\n", $beforeCode);  
  
        $afterCode = [];  
        $afterCode[] = <<<PHP  
  
                    <?php   
                    \$phad->rowFinished($itemDataVar, $itemVar);  
                endforeach;  
        PHP;  
  
        $hasAccessIf = false;  
        foreach ($statusNodeData as $accessStatus=>$accessIndexList){  
            if ($accessStatus==200)continue;  
            $afterCode[] = <<<PHP  
                elseif (${itemDataVar}->accessStatus == $accessStatus):  
            PHP;  
            $if = 'if';  
            // $count = count($accessIndexList);  
            // $iters = 0;  
            $if_iters = 0;  
            foreach ($accessIndexList as $accessIndex=>$actualCode){  
                // $iters++;  
                if (is_numeric($accessIndex)){  
                    $if_iters++;  
                    // $endif = $iters < $count  
                        // ? 'endif; //close if (\$NamedItem->accessIndex == ###)'  
                        // : '// no endif, expecting else';  
                    $afterCode[] = <<<PHP  
                            ${if} (${itemDataVar}->accessIndex == $accessIndex): $closeTag  
                                $actualCode  
                            <?php  
                    PHP;  
                    $afterCode[] = '        endif; // added to close accessIndex if()';  
                    $if = 'elseif';  
                    $hasAccessIf = true;  
                } else if ($accessIndex=='else'){  
                      
                    if ($if_iters>0)array_pop($afterCode);  
                    $else = $hasAccessIf ? 'else: ' : '';  
                    $afterCode[] = <<<PHP  
                            $else $closeTag  
                                $actualCode  
                            <?php  
                            endif; // close else: within access branches  
                    PHP;  
                    // $hasAccessIf = true;  
                }   
            }  
  
        }  
        $accessIf = $hasAccessIf ? 'endif; //idk' : '';  
        $accessIf = '//endif; removed';  
        $afterCode[] =   
            <<<PHP  
                    $accessIf  
                endif; // close sequence of elseif (\$NamedItem->accessStatus == ###):  
          
                \$phad->itemListFinished($itemDataVar);  
            PHP  
        ;  
        $afterCode[] = '?>';  
        $afterCode = implode("\n", $afterCode);  
  
    }  
  
}