Login   Register  
PHP Classes
elePHPant
Icontem

File: Xyndravandria/Averazain/Averazain.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Mauro Di Girolamo  >  Xyndravandria Averazain  >  Xyndravandria/Averazain/Averazain.php  >  Download  
File: Xyndravandria/Averazain/Averazain.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Xyndravandria Averazain
Handle AJAX requests with PHP classes
Author: By
Last change: Alpha 0.0.0
Date: 2013-05-22 13:23
Size: 17,645 bytes
 

Contents

Class file image Download
<?php
/*
=============================================================================================================================================
|   This file is part of a project released under the terms of the Xyndravandria PHP License (XyndravandriaPHPLicense.txt).                 |
|                                                                                                                                           |
|   You should be given a copy of the Xyndravandria PHP License (XyndravandriaPHPLicense.txt) within the same directory as the README.md;   |
|   if not, you can get a copy at http://Xyndravandria.ohost.de/XyndravandriaPHPLicense.txt .                                               |
|                                                                                                                                           |
|   The copyright (c) of this project is owned by Mauro Di Girolamo <maurodigirolamo@.web.de>.                                              |
============================================================================================================================================|



Xyndravandria Averazain
-----------------------
Alpha 0.0.0

Xyndravandria is the name of a collection of projects designed and developed by Mauro Di Girolamo (maurodigirolamo@web.de); he is therefore the copyright (c) owner of Xyndravandria itself and all of its projects.

Xyndravandria Averazain is released under the terms of the Xyndravandria PHP License (XyndravandriaPHPLicense.txt). You should be given a copy of the Xyndravandria PHP License (XyndravandriaPHPLicense.txt) within the same directory as the README.md; if not, you can get a copy at http://Xyndravandria.ohost.de/XyndravandriaPHPLicense.txt . There might be a release under a freer license for a later, more stable version.

The documentation is either included in ./admin_media/Documentation/ or can be read at http://Xyndravandria.ohost.de/Averazain/Documentation/.

All projects:

   Xyndravandria Averazain
   http://github.com/MauroDiGirolamo/Xyndravandria_Averazain
   PHP
   Averazain is an Ajax framework supporting also JavaScript disabled clients perfectly - including search engines like Google.
   
   Xyndravandria Dyverath
   http://github.com/MauroDiGirolamo/Xyndravandria_Dyverath
   PHP
   Dyverath is a database access wrapper.
   
   Xyndravandria Erozaver
   http://github.com/MauroDiGirolamo/Xyndravandria_Erozaver
   PHP
   Erozaver is a class extending the type hinting given by the PHP engine (additional support for basic type hinting and size constraints).
   
   Xyndravandria Mondraviel
   http://github.com/MauroDiGirolamo/Xyndravandria_Mondraviel
   PHP
   Mondraviel is a class used to separate HTML from PHP code by firstly register models - files containing place holders embedded in HTML code - and then later fill them dynamically with content by passing values for the place holders.
*/

namespace Xyndravandria\Averazain;

// TODO: Thing about enabling settype( ) again.
// TODO: Display errors in requests.
/// @ref Averazain "Averazain's" main class.
/// @abstract
abstract class Averazain {

   /// Holds all the class names which have been
   /// registered to Averazain using RegisterClass( ).
   /// <dl class = "type"><dt><b>%Type:</b></dt>
   /// <dd>array of string</dd></dl>
   /// @private 
   /// @static      
   private static $ClassRegister = array( );

   /// Registers a class to Averazain.
   /// @public
   /// @static      
   /// @param string $ClassName: The class name to be
   /// registered.
   public static function RegisterClass( $ClassName ) {
      //\settype( $ClassName, 'string' );
      if( ! \class_exists( $ClassName ) )
         throw new XyndravandriaAverazainException( 'The to be registered class \'' . $ClassName . '\' is not defined.' );
      else {
         $ReflectionClass = new \ReflectionClass( $ClassName );
         if( ! $ReflectionClass->implementsInterface( 'Xyndravandria\Averazain\PageCollection' ) )
            throw new XyndravandriaAverazainException( 'The given class \'' . $ReflectionClass->name . '\' does not implement the Xyndravandria\Averazain\PageCollection interface.' );
         else
            \array_push( self::$ClassRegister, $ReflectionClass->name );
         return;
      }
   }

   /// Holds the JavaScript generated by Averazain.
   /// <dl class = "type"><dt><b>%Type:</b></dt>
   /// <dd>string</dd></dl>
   /// @private 
   /// @static      
   private static $JavaScript = '';
   
   /// Returns Averazain::$JavaScript.
   /// @public
   /// @static      
   /// @returns string
   public static function JavaScript( ) {
      return self::$JavaScript;
   }
   
   /// Holds the form data which has been transferred
   /// through an Averazain request. @n
   /// <dl class = "type"><dt><b>%Type:</b></dt>
   /// <dd>array of mixed</dd></dl>
   /// @private 
   /// @static      
   private static $FormData = array( );
   
   /// Returns either one element of Data::$FormData or
   /// the whole attribute.
   /// @public
   /// @param integer $Index: Index of an
   /// element inside the Data::$FormData array.
   /// @returns array of mixed or mixed or null
   /// @note $Index is an optional parameter. @n
   /// If not passed, Data::$FormData will be returned.
   public static function FormData( $Index = null ) {
      //\settype( $Index, 'integer' );
      if( \is_null( $Index ) )
         return self::$FormData;
      else 
         return isset( self::$FormData[ $Index ] ) ? self::$FormData[ $Index ] : null;
   }
   
   /// Used to influence the behaviour of
   /// Averazain::Execute( ).
   const AutomaticallyRegisterClasses = 1;

   /// <ul>
   ///   <li>
   ///       The behaviour can be influenced using the
   ///       $Operation parameter.
   ///   </li>
   ///   <li>
   ///       Calls ExtractFormData( ).
   ///   </li>
   ///   <li>
   ///       Calls RetrieveAnchor( ) to check whether
   ///       there is an anchor and if yes, handles it.
   ///   <li>
   ///       Calls ProceedRequest( ) to check whether
   ///       Averazain was called by an Ajax request and
   ///       if yes, handles it.
   ///   </li>
   ///   <li>
   ///       Calls GenerateJavaScript( ).
   ///   </li>
   /// </ul>
   /// @public       
   /// @static      
   /// @param integer $Operation: Used to influence the
   /// behaviour of Averazain::Execute( ). @
   /// Averazain::AutomaticallyRegisterClasses - defines
   /// whether Averazain should automatically register
   /// all classes implementing PageCollection or not. If
   /// not defined, this has to be done manually using
   /// Averazain::RegisterClass( ).
   /// @note This method has to be called before any
   /// output is sent to the client!
   public static function Execute( $Operation = 0 ) {
      //\settype( $Operation, 'integer' );
      if( $Operation & Averazain::AutomaticallyRegisterClasses )
         foreach( \get_declared_classes( ) as $ClassName ) {
            $ReflectionClass = new \ReflectionClass( $ClassName );
            $ReflectionClass->implementsInterface( 'Xyndravandria\Averazain\PageCollection' ) && \array_push( self::$ClassRegister, $ReflectionClass->name );
         }
      #   self::GenerateJavaScript( );
      self::ValidateRegisteredClasses( );
      self::ExtractFormData( );
      self::RetrieveAnchor( );
      self::ProceedRequest( );
      self::GenerateJavaScript( ); // TODO: Order important (put JavaScript at the end)?
      return;
   }
   
   /// Validates all registered classes and fills
   /// Averazain::$Anchor.
   /// @private
   /// @static
   private static function ValidateRegisteredClasses( ) {
      foreach( self::$ClassRegister as $ClassName )
         foreach( \get_class_methods( $ClassName ) as $Method )
            if( \in_array( $Method, array( 'Authorise', 'Target', 'Anchor' ) ) ) // TODO: array_diff( ) refuses to work.
               continue;
            else {
               // TODO: Validate $Target and $Anchor.
               $Target = $ClassName::Target( $Method ); // TODO: Check BEFORE calling whether the methods exist?
               $Anchor = $ClassName::Anchor( $Method );
               if( ! \is_null( $Anchor ) && isset( self::$Anchor[ $Anchor ] ) )
                  throw new XyndravandriaAverazainException( 'The anchor \'' . $Anchor . '\' for method \'' . $Method . '\' of the class \'' . $ClassName . '\' has already been defined earlier for method \'' . self::$Anchor[ $Anchor ]->Method . '\' of class \'' . self::$Anchor[ $Anchor ]->Class . '\'.' );
               else
                  \is_null( $Anchor ) || self::$Anchor[ $Anchor ] = ( object )array( 'Class' => $ClassName, 'Method' => $Method );
            }
      return;
   }
   
   /// Extracts the form data if sent and saves it into
   /// Averazain::$FormData.
   /// @private
   /// @static
   private static function ExtractFormData( ) {
      foreach( $_POST as $Index => $Value )
         if( \preg_match( '#^Xyndravandria->Averazain->Request->FormData->(.+)#', $Index, $Match ) )
            self::$FormData[ $Match[ 1 ] ] = self::Demask( $Value ); // TODO: Demask here correct?
      return;
   }
   
   /// The associations between anchors and method are
   /// saved within this array.
   /// <dl class = "return"><dt><b>%Type:</b></dt>
   /// <dd>array of array of string</dd></dl>
   /// @private 
   /// @static
   private static $Anchor = array( );
   
   /// The anchor retrieved by Averazain.
   /// <dl class = "return"><dt><b>%Type:</b></dt>
   /// <dd>null or array of mixed</dd></dl>
   /// @private 
   /// @static
   private static $RetrievedAnchor = null;
      
   /// Returns Averazain::$RetrievedAnchor.
   /// @public 
   /// @static
   /// @returns null or array of mixed
   public static function RetrievedAnchor( ) {
      return self::$RetrievedAnchor;
   }
   
   /// Checks whether there is an anchor retrieved.
   /// @public
   /// @static
   /// @returns boolean
   public static function AnchorRetrieved( ) {
      return ! \is_null( self::$RetrievedAnchor );
   }

   /// Calls the method of the retrieved anchor and
   /// returns its output.
   /// @public
   /// @static
   /// @returns mixed
   public static function CallAnchoredMethod( ) {
      if( \is_null( self::$RetrievedAnchor ) )
         throw new XyndravandriaAverazainException( 'There is no anchored method!' );
      else
         return \forward_static_call_array( array( self::$RetrievedAnchor->Class, self::$RetrievedAnchor->Method ), self::$RetrievedAnchor->Argument );
      return;
   }
   
   /// Retrieves the anchor if it exists and then either
   /// returns the associated method's output directly to
   /// the client or saves the anchor into
   /// Averazain::$RetrievedAnchor.
   /// @private
   /// @static
   private static function RetrieveAnchor( ) {
      foreach( $_GET as $Hash => $Value )
         if( preg_match( '#^(?<Anchor>[A-Za-z.]+)(:(?<Argument>.+))?$#', $Hash, $Match ) && isset( self::$Anchor[ $Match[ 'Anchor' ] ] ) )
            if( isset( $_GET[ 'Request' ] ) ) {
               $_POST[ 'Xyndravandria->Averazain->Request->Class' ] = self::$Anchor[ $Match[ 'Anchor' ] ]->Class;
               $_POST[ 'Xyndravandria->Averazain->Request->Method' ] = self::$Anchor[ $Match[ 'Anchor' ] ]->Method;
               $_POST[ 'Xyndravandria->Averazain->Request->Argument' ] = ( isset( $Match[ 'Argument' ] ) && ! empty( $Match[ 'Argument' ] ) ) ? $Match[ 'Argument' ] : '';
               try {
                  self::ProceedRequest( );
               } catch( Exception $Exception ) {
               }
            } else {
               self::$RetrievedAnchor = ( object )array_merge( ( array )self::$Anchor[ $Match[ 'Anchor' ] ], array( 'Argument' => isset( $Match[ 'Argument' ] ) && ! empty( $Match[ 'Argument' ] ) ? \explode( ',', $Match[ 'Argument' ] ) : array( ), 'Anchor' => $Match[ 'Anchor' ] ) );
               return;
            }
      if( isset( $_GET[ 'Request' ] ) )
         exit;
      return;
   }
   
   /// Checks whether Averazain was called by an Ajax
   /// request and if yes, sends the demanded method's
   /// return value back to the client as an Ajax
   /// response which is, again, handled by Averazain.
   /// @private
   /// @static
   /// @note Calls Averazain::ExtractUniqueMethods( ).
   private static function ProceedRequest( ) {
      if( \headers_sent( $File, $Line ) )
         throw new XyndravandriaAverazainException( 'Output has already been sent to the client in the file ' . $File . ' on line ' . $Line . '. Please ensure Averazain::Execute( ) is called before any output is sent to the client.' );
      else {
         if( isset( $_POST[ 'Xyndravandria->Averazain->Request->Class' ] ) && isset( $_POST[ 'Xyndravandria->Averazain->Request->Method' ] ) ) {
            $Class = $_POST[ 'Xyndravandria->Averazain->Request->Class' ];
            $Method = $_POST[ 'Xyndravandria->Averazain->Request->Method' ];
            if( ! \class_exists( $Class ) )
               throw new XyndravandriaAverazainException( 'Unknown class \'' . $Class . '\'.' );
            elseif( ! $Class::Authorise( $Method ) )
               throw new XyndravandriaAverazainException( 'Access denied for method \'' . $Method . '\' of class \'' . $Class . '\'.' );
            elseif( ! \method_exists( $Class, $Method ) )
               throw new XyndravandriaAverazainException( 'The \'' . $Class . '\' class does not have a method called \'' . $Method . '\'.' );
            else {
               $Argument = array( );
               isset( $_POST[ 'Xyndravandria->Averazain->Request->Argument' ] ) && $Argument = empty( $_POST[ 'Xyndravandria->Averazain->Request->Argument' ] ) ? array( ) : \explode( ',', $_POST[ 'Xyndravandria->Averazain->Request->Argument' ] );
               foreach( $Argument as &$Value ) // TODO: Remove reference?
                  self::Demask( $Value );
               echo $ResponseText = \forward_static_call_array( array( $Class, $Method ), $Argument );
               // TODO: Possible conflict when addslashes( ) escapes also double quotes?
               $Target = $Class::Target( $Method );
               self::TargetAttached( $ResponseText ) || print \is_null( $Target ) ? '$null' : '$' . \addslashes( $Target );
               exit( );
            }
         }
         return;
      }
   }
   
   /// Turns the registered PHP classes in the @ref
   /// $ClassRegister and its methods into correspondent
   /// JavaScript classes and methods which send an
   /// Ajax request to Averazain demanding to call the
   /// original PHP method and send its return value back
   /// to the client as an Ajax response which is, again,
   /// handled by Averazain.
   /// @private
   /// @static      
   private static function GenerateJavaScript( ) {
      self::$JavaScript = '<script type = "text/javascript">' . \file_get_contents( __DIR__ . '/XyndravandriaAverazain.js' ) . '
';
      foreach( self::$ClassRegister as $ClassName ) {
         self::$JavaScript .= 'function ' . $ClassName . '( ) {
}
';
         foreach( \get_class_methods( $ClassName ) as $Method )
            if( \in_array( $Method, array( 'Authorise', 'Target', 'Anchor' ) ) ) // TODO: array_diff( ) refuses to work.
               continue;
            else {
               // TODO: Validate $Target and $Anchor.
               $Target = $ClassName::Target( $Method ); // TODO: Check BEFORE calling whether the methods exist?
               $Anchor = $ClassName::Anchor( $Method );
               self::$JavaScript .= $ClassName . '.' . $Method . ' = function( ) { ' . "\n";
               \is_null( $Anchor ) || self::$JavaScript .= '   XyndravandriaAverazain.SetHash( \'' . \addslashes( $Anchor ) . '\', arguments );' . "\n";
               self::$JavaScript .= '   XyndravandriaAverazain.Request( \'POST\', \'' . $ClassName . '\', \'' . $Method . '\', arguments );' . "\n" .
               '   return;' . "\n" .
               '};' . "\n";
            }
         self::$JavaScript .= '
';
      }
      self::$JavaScript .= 'XyndravandriaAverazain.OnInit( );' . "\n";
      if( ! \is_null( self::$RetrievedAnchor ) && ! isset( $_GET[ 'Request' ] ) ) {
         list( $HashListing, ) = HTMLBuilder::ArgumentListing( self::$RetrievedAnchor->Argument );
         self::$JavaScript .= 'var URL = location.href.split( \'?\' )' . "\n" .
         'location.href = URL[ 0 ] + \'#' . self::$RetrievedAnchor->Anchor . ( empty( $HashListing ) ? '' : $HashListing ) . '\';' . "\n";
      }
      self::$JavaScript .= '</script>';
      return;
   }
   
   /// Demasks a given value sent within an Averazain request
   /// @private 
   /// @static      
   /// @param string & $Value: The value to be demasked.
   /// @returns string
   private static function Demask( & $Value ) {
      foreach( array( '[a]' => "\n", 
                      '[b]' => '&',
                      '[c]' => ',', 
                      '[d]' => ':',
                      '[e]' => '#',
                      '[f]' => '=' ) as $Escape => $Digit )
         $Value = \str_replace( $Escape, $Digit, $Value );
      return $Value;
   }
   
   /// Checks whether a target is already attached to a
   /// response text.
   /// @private
   /// @static
   /// @param string $ResponseText: The response text to be
   /// checked.
   /// @returns boolean
   private static function TargetAttached( $ResponseText ) {
      if( ! empty( $ResponseText ) ) {
         $a = \strlen( $ResponseText );
         while( --$a > 0 && $ResponseText[ $a ] != '$' );
         return $ResponseText[ $a ] == '$';
      } else
         return false;
   }
   
}
?>