<?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;
}
}
?>
|