Controller.php

<?php

namespace ROF\View;

class Controller {
  
  static public function addScript($scriptPath){
      static::$stScripts = array_unique(
        array_merge(static::$stScripts, [$scriptPath])
      );
  }
  static public function addStyle($stylePath){
      static::$stStyles = array_unique(
        array_merge(static::$stStyles, [$stylePath])
      );
  }
  static protected $stStyles = [];
  static protected $stScripts = [];
  
  private $content = "";
  
  private $originalContent = [];
  private $scripts = [];
  private $stylesheets = [];
  private $title;
  private $keywords;
  private $description;
  private $image;
  private $author;
  private $documentRoot;
  private $url;
  private $type;
  private $fb_app_id;
  
  
  public function __construct($documentRoot){
    include_once(__DIR__.'/../lib/simple_html_dom.php');
    $this->documentRoot = $documentRoot;
  }
  
  public function load($content){
      $this->content = $content;
      
      $dom = new \simple_html_dom($this->content);
      $c=0;
      while ($c++<30&&count($dom->find('view'))>0){
          $this->singlePass();
          $dom = new \simple_html_dom($this->content);
      }
  }
  
  private function insertHeadHtml(){
      $dom = new \simple_html_dom($this->content);
      $outputTags = $dom->find("view[type=output], view[name=Head]");
      $staticStyles = static::$stStyles;
      static::$stStyles = [];
      $this->stylesheets = array_unique(array_merge($this->stylesheets,$staticStyles));
      $staticScripts = static::$stScripts;
      static::$stScripts = [];
      $this->scripts = array_unique(array_merge($this->scripts,$staticScripts));
      if (count($outputTags)==0){
          $stylesList = '';
          foreach ($this->stylesheets as $stylesheet){
              if (!file_exists($stylesheet))$stylesheet = ($this->documentRoot.'/Style/'.$stylesheet);
              if (!file_exists($stylesheet))throw new \Exception("Stylesheet __".$stylesheet."__ does not exist");
              $stylesList .= $stylesheet.',';
          }
          $scriptList = '';
          foreach ($this->scripts as $script){
              if (!file_exists($script))$script = ($this->documentRoot.'/Script/'.$script);
              if (!file_exists($script))throw new \Exception("Stylesheet __".$script."__ does not exist");
              $scriptList .= $script.',';
          }
          $dataTag = '<view type="self" scripts="'.$scriptList.'" stylesheets="'.$stylesList.'"></view>';
          $this->content = $dataTag.$this->content;
      } else {
          $this->insertHeadHtml2();
      }
      
  }
  
  private function insertHeadHtml2(){
    // Collects the stylesheets and compiles them into a single, minified file
    // Collects the scripts and compiles them into a single, minified file.
    // Returns SEO information 
    // Returns viewport meta tag
    
    $outputHtml = "";
    
    $stylesheet = "";
    $stylesName = "css-";
    $mostRecentEdit = 0;
    foreach ($this->stylesheets as $fileName){
      if (file_exists($fileName)){
        $file = $fileName;
        $fileName = basename($fileName);
      } else {
        $fileName = basename(parse_url($fileName,PHP_URL_PATH));
        $file = $this->documentRoot.'/style/'.$fileName;
      }
      $stylesName .= $fileName;
      if (file_exists($file)){
        $editTime = filemtime($file);
        if ($editTime>$mostRecentEdit)$mostRecentEdit = $editTime;
        if (substr($file,-4)==='.php'){
            ob_start();
            include($file);
            $contents = ob_get_clean();
        } else {
            $contents = file_get_contents($file);
        }
        $stylesheet .= $contents;
      } else {
        
        $stylesheet = "/* Stylesheet {$file} was not found */\n".$stylesheet;
      }
    }
    $stylesName = str_replace(array('.css','.','?','#',' '),array('-','_','v','h',''),$stylesName);
    $stylesName = strtolower(substr($stylesName,0,251).'.css');
    $stylesDir = $this->documentRoot.'/view/compiled/';
    if (!file_exists($stylesDir))mkdir($stylesDir,0755);
    $stylesPath = strtolower($stylesDir.$stylesName);
    if (!file_exists($stylesPath)||$mtime=filemtime($stylesPath)<$mostRecentEdit){
      file_put_contents($stylesPath,$stylesheet);
    }
    $outputHtml .= '<link rel="stylesheet" mtype="test/css" href="/compiled/'.$stylesName.'?'.filemtime($stylesPath).'" />'."\n";
    
    $scripts = "";
    $scriptsName = "js-";
    $mostRecentEdit = 0;
    foreach ($this->scripts as $fileName){
        if (file_exists($fileName)){
        $file = $fileName;
        $fileName = basename($fileName);
      } else {
        $fileName = basename(parse_url($fileName,PHP_URL_PATH));
        $file = $this->documentRoot.'/script/'.$fileName;
      }
      $scriptsName .= $fileName;
      if (file_exists($file)){
        $editTime = filemtime($file);
        if ($editTime>$mostRecentEdit)$mostRecentEdit = $editTime;
        if (substr($file,-4)==='.php'){
            ob_start();
            include($file);
            $contents = ob_get_clean();
        } else {
            $contents = file_get_contents($file);
        }
        $scripts .= $contents;
      } else {
        
        $scripts = "/* Script {$file} was not found */\n".$scripts;
      }
    }
    $scriptsName = str_replace(array('.js','.','?','#',' '),array('-','_','v','h',''),$scriptsName);
    $scriptsName = strtolower(substr($scriptsName,0,252).'.js');
    $scriptsDir = $this->documentRoot.'/view/compiled/';
    if (!file_exists($scriptsDir))mkdir($scriptsDir,0755);
    $scriptsPath = strtolower($scriptsDir.$scriptsName);
    if (!file_exists($scriptsPath)||filemtime($scriptsPath)<$mostRecentEdit){
      file_put_contents($scriptsPath,$scripts);
    }
    $outputHtml .= '<script type="text/javascript" src="'.'/compiled/'.$scriptsName.'"></script>'."\n";
    
    if ($this->title!=NULL){
      $outputHtml .= '<title>'.$this->title.'</title>'."\n"
                      .'<meta property="og:title" content="'.$this->title.'" />'."\n";
    }
    if ($this->description!=NULL){
      $outputHtml .= '<meta property="og:description" name="description" content="'.$this->description.'">'."\n";
    }
     if ($this->keywords!=NULL){
      $outputHtml .= '<meta property="og:keywords" name="keywords" content="'.$this->keywords.'">'."\n";
    }
    if ($this->image!=NULL){
      $outputHtml .= '<meta property="og:image" content="'.$this->image.'" />'."\n";
    }
    if ($this->url==NULL){
      $this->url = $this->getFullUrl('');
    }
    if ($this->url!=NULL){
      $outputHtml .= '<meta property="og:url" content="'.$this->url.'" />'."\n";
    }
    if ($this->type!=NULL){
      $outputHtml .= '<meta property="og:type" content="'.$this->type.'" />'."\n";
    }
    if ($this->fb_app_id!=NULL){
      $outputHtml .= '<meta property="fb:app_id" content="'.$this->fb_app_id.'" />'."\n";
    }
    $dom = new \simple_html_dom($this->content);
    $outputTags = $dom->find("view[type=output], view[name=Head]");
    $c =0;
    foreach ($outputTags as $output){
      if ($c++>0)throw new \Exception("Error preparing the page..... There were multiple head output views. Only one is allowed. Error reported on line "
                                .__LINE__." of file ".__FILE__);
      $output->outertext = $outputHtml;
    }
    $this->content = $dom.'';
    //output tags are for inserting the head data, so that PHP doesn't have to be written in the html <head> tag. 
    //Instead a <view type="output"> is put into the head tag, then it is filled in by this script
  }
  
  private function discoverInfo($dom){
    $this->stylesheets = array_unique(
        array_merge($this->stylesheets, $dom->stylesheets!=NULL ? explode(',',$dom->stylesheets) : array())
      );
    $this->scripts = array_unique(
        array_merge($dom->scripts!=NULL ? explode(',',$dom->scripts) : array(),$this->scripts)
      );
    if ($this->title==NULL&&$dom->title!=NULL){
      $this->title = $dom->title;
    }
    if ($this->image==NULL&&$dom->image!=NULL){
      $this->image = $this->getFullUrl($dom->image);
    }
    if ($this->description==NULL&&$dom->description!=NULL){
      $this->description = $dom->description;
    }
    if ($this->keywords==NULL&&$dom->keywords!=NULL){
      $this->keywords = $dom->keywords;
    }
    if ($this->url==NULL&&$dom->url!=NULL){
      $this->url = $this->getFullUrl($dom->url);
    }
    if ($this->type==NULL&&$dom->ogtype!=NULL){
      $this->type = $dom->ogtype;
    }
    if ($this->fb_app_id==NULL&&$dom->fb_app_id!=NULL){
      $this->fb_app_id = $dom->fb_app_id;
    }
  }
  
  private function singlePass(){
    
    $dom = new \simple_html_dom($this->content);
    $headerData = count($_GET)>0 ? $_GET : $_POST;
    if (isset($headerData['view_type'])&&strtolower($headerData['view_type'])=='none'){
        $viewTags = $dom->find('view');
        if (count($viewTags)==1&&trim($viewTags[0]->outertext)==trim($dom->outertext)){
            $this->content = $viewTags[0]->innertext;
        } else {
            foreach ($viewTags as $tag){
                $tag->outertext = '<span>'.$tag->innertext.'</span>';
            }
            $this->content = $dom->outertext;
        }
        
        return;
    }
    
    $parentTags = $dom->find('view[type=parent]');
    $includeTags = $dom->find('view[type=include]');
    $childTags = $dom->find('view[type=child]');
    $selfTags = $dom->find('view[type=self]');
    $dataTags = $dom->find('view[type=data]');
    //self tags can contain information about stylesheets, scripts, and meta data.
    //self tags provide no visual information. They are data only and the tag must be removed.
    
    foreach ($selfTags as $self){
      //echo $self;exit;
      $this->discoverInfo($self);
      $self->outertext = $self->innertext;
    }
    static $offset = 0;
    foreach ($childTags as $count=>$child){
      $this->discoverInfo($child);
      $child->outertext = $this->originalContent[$count+$offset];
    }
   // $this->originalContent = array();
    foreach ($parentTags as $parent){
      $this->discoverInfo($parent);
      $fileContent = $this->getViewFile($parent->name);
      $this->originalContent[] = $parent->innertext;
      $parent->outertext = $fileContent;
    }
    foreach ($includeTags as $included){
      $this->discoverInfo($included);
      $fileContent = $this->getViewFile($included->name);
      $included->outertext = $fileContent;
    }
    
    $this->content = $dom->outertext;
  }
  
  private function getViewFile($viewName){
    if ($viewName==FALSE){
      throw new \Exception("View Name is empty, so the page can not be created. "
                           ."Web Developer: Please see the script for this page and correct the view tag.");
    }
    $filePath = $this->documentRoot.'/template/'.$viewName.'.php';
    if (!file_exists($filePath)){
      throw new \Exception("There has been an error creating the page...... View '{$viewName}' could not be found on the server at path '{$filePath}'. "
                      ."Error reported on line ".__LINE__." in file ".__FILE__);
    }
      ob_start();
      include($filePath);
    $fileContent = ob_get_clean();
    return $fileContent;
  }
  
  public function display(){
    $this->insertHeadHtml();
    $dom = new \simple_html_dom($this->content);
    $residualTags = $dom->find("view");
    foreach ($residualTags as $tag){
        $tag->outertext = $tag->innertext;
    }
    echo $dom->outertext;
  }
  
  /** $path may be relative or the full url. This function will determine and return an address with protocol://domain/relative_path
  *   if $path starts with '/' then it will be relative to the protocol://domain
  *   if $path does NOT start with '/' then it will be relative to the current directory
  */
  public function getFullUrl($path){
    $protocol = isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] === 1) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ? 'https' : 'http';
      $domain = $_SERVER['HTTP_HOST'];
    $start = $protocol.'://'.$domain;
    
    if (strpos($path,'/')!==0){
      $path = str_replace('//','/',substr($_SERVER['REQUEST_URI'],0,strrpos($_SERVER['REQUEST_URI'],'/')).'/'.$path);
    }
    if (strpos($path,$start)!==0){
      $path = $start.$path;
    }
    return $path;
  }
  
  public function displayView($viewName){
      $content = $this->getViewFile($viewName);
      $this->load($content);
      $this->display();
  }
  
}



?>