PageRenderTime 79ms CodeModel.GetById 27ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 1ms

/x/lib/x4deep/xengine.php

https://github.com/xopherdeep/Skeleton-App
PHP | 1432 lines | 1020 code | 191 blank | 221 comment | 122 complexity | 4dc837d8a29305b6c0c396ec4cec0a9c MD5 | raw file
   1<?php
   2	/**
   3	 * Xengine
   4	 * @author XopherDeeP <heylisten@xtiv.net>
   5	 * @version 1.0.0-rc1.0.1
   6	**/
   7	# Xengine is Small and PowerFull; Hook into it using Xtras. The Xengine Idea is: Drop and Build!
   8	# It setups an easy to use connection between Clean URLs and PHP Classes
   9	# The eXtend PHP Classes with Xengine allowing Easy Access to the the DB using $q = $this->q(); 
  10	# The DB doesn't connect until the function is called, once connected, it can be accessed via $this->Q
  11	# The CleanURL Structure is as Follows: domain.com/class/method/param1/param2/param3/etc
  12	# where class is the class name found in the Xtra's. ex: xClass.php, the method should be found there.
  13	# If there is an HTML page to render with the method, the files go in the html dir, 
  14
  15	define('DOC_ROOT'	,$_SERVER['DOCUMENT_ROOT']);
  16	define('LIBS_DIR'	,DOC_ROOT.'/x/lib'); 			# Location of the Library Files
  17	define('XPHP_DIR'	,DOC_ROOT.'/x/xtra'); 			# Location of the Xtras Files
  18	define('BIN'		,DOC_ROOT.'/bin'); 				# Location of the Bin Files
  19
  20	
  21	set_include_path(DOC_ROOT.'/x/lib');
  22	
  23	class Xengine
  24	{
  25		var $_CFG;
  26		var $_SET = array(
  27			'action'	=> '',
  28			'method'	=> '',
  29			'HTML' => array(
  30				'HEAD' => array(
  31					'TITLE' => null,
  32					'CSS'   => '',
  33					'JS'    => '',
  34					'STYLE' => ''
  35				),
  36				'BODY' => array(
  37					'HTML' => '',
  38					'CSS'  => '',
  39					'JS'   => ''
  40				)
  41			)
  42		);
  43		var $_DBF;
  44		var $Xtra;
  45		var $method;
  46		var $_debugReport = array();
  47
  48		function __construct($cfg = false,$parent = false)
  49		{
  50
  51				
  52			$this->_comment("Xengine Started!");
  53			ini_set('display_errors', $cfg['debug']['on']);
  54
  55			if(!$cfg)
  56				die("Configuration Needed");
  57
  58			// We need to be able to write to this directory...
  59			if(!is_writeable($cfg['dir']['cfg']))
  60				die($cfg['dir']['cfg']." Not Writable!");
  61
  62			$this->_CFG   = $cfg;
  63			$this->_LANG  = $cfg['lang'] ;
  64			$this->_BOTS  = $cfg['bots_list'];
  65
  66			if(!defined('DB_CFG'))
  67				define("DB_CFG", $cfg['dir']['cfg']."/cfg.db.$_SERVER[HTTP_HOST].inc");
  68		}
  69
  70
  71		/*
  72			The Client has 'Knocked' on the website's 'Door'
  73			Identify the whole: user, location, content, module, timestamp
  74			Decide what to do with them. 
  75		*/
  76		public function knock()
  77		{
  78			try {
  79				register_shutdown_function(array( &$this, "shutDownFunction" ));
  80
  81				$this->keyhole();											// Identify the Whole.
  82				$this->openDoor();											// Open the Door a.k.a Execute Mods.
  83				$this->browse(); 											// Display the Content, HTML, JSON, XML, JPEG.
  84				exit;														// EXIT
  85			}catch(Exception $e){ 
  86				$this->reportSystemError(array(
  87					'summary'     => 'X ERROR :: '.$e->getMessage(), 
  88					'description' => $e->getMessage(),
  89					'attr' => array(
  90						'type'      => 'defect',
  91						'component' => 'x'.ucfirst($this->_SET['action']),
  92						'priority'  => 'trivial',
  93						'reporter'  => $_SESSION['user']['username'].'@'.$_SERVER['HTTP_HOST'],
  94						'keywords'  =>  $this->_SET['action'].'::'. $this->_SET['method'],
  95						'milestone' => 'Bee Hive'
  96					)
  97    			));
  98				$this->set(array(
  99					'action' => 'access',
 100					'method' => 'error',
 101					'params' => array(
 102						'error' => $e->getMessage()
 103					),
 104					'request' => array(
 105						'action' => $this->_SET['action'],
 106						'method' => $this->_SET['method']
 107					)
 108				));
 109				//$this->dump($this->_SET);
 110				$this->browse(); 
 111			}
 112		}
 113
 114		private function keyhole()
 115		{
 116			session_start();
 117
 118			// Who Am I?
 119			$this->whoAmI();
 120			
 121			// Where Am I?
 122			$this->whereAmI();
 123 			
 124 			// What Content Type am I
 125			$this->whatAmI();
 126			
 127			// How Am I Built
 128 			$this->howAmI();
 129
 130			// When Am I Happening
 131			// $this->whenAmI();
 132			
 133			// Why Am I Existing
 134			// $this->whyAmI();
 135 			
 136 			
 137		}
 138
 139		// The Key holds an array of Bool
 140		private function whoAmI($who=false)
 141		{
 142			// Define Their KEY - Refrenced for Access. 
 143			$this->Key = array(
 144				'is' => array(
 145					'admin'   => isset($_SESSION['user']) ?  ($_SESSION['user']['power_lvl'] > 7)  : false,
 146					'user'    => (isset($_SESSION['user']) && !empty($_SESSION['user'])) ,
 147					'guest'   => ( isset($_SESSION['user'] ) != true),
 148					'bot'     => ( preg_match('/'.implode("|", $this->_BOTS).'/', $_SERVER['HTTP_USER_AGENT'], $matches) ) ? 
 149						array_search($matches[0], $bots_list) : false
 150				)
 151			); 
 152
 153		}
 154
 155		private function whereAmI()
 156		{
 157			// This Function Sets all the variables on where the Client IS based on the URL they've Hit.
 158			$this->uri         = substr( $_SERVER['REQUEST_URI'], 1 );	// Begins with a / - slice it off.
 159			$this->url         = parse_url( $this->uri );				
 160			$this->_SET['params']        = (isset($this->url['path'])) ? explode('/', $this->url['path']) : array('/');
 161			
 162			
 163			if(!isset($this->_SET['params'][1])){
 164				$this->_SET['params'] = array('','');
 165			}
 166
 167			// BOOL
 168			$this->atSideDoor  = ($this->_SET['params'][0] === $this->_CFG['dir']['sidedoor'] 
 169				|| $this->_SET['params'][1] === $this->_CFG['dir']['sidedoor']);	
 170
 171			// Back Door - Admin Panel of Pages. 
 172			$this->atBackDoor  = ($this->_SET['params'][0] === $this->_CFG['dir']['backdoor']);	// BOOL
 173
 174
 175			$this->atMailBox   = ($this->_SET['params'][0] === $this->_CFG['dir']['bin']);		// BOOL
 176			
 177		}
 178
 179		public function whatAmI()
 180		{
 181			// Let's Discover if this document is looking for an extension
 182			$e = explode('.', $this->url['path']);
 183			$this->Key['is']['content'] = (count($e) > 1) ? $e[count($e)-1] : 'html';
 184		}
 185
 186		// Here we determine what it is the client is trying to access. 
 187		private function howAmI()
 188		{	
 189			// ../Xtra/method/param1/param2/param3/etc	
 190			$p = $this->_SET['params'];
 191			$this->_SET['action'] = $this->_SET['method'] = 'index';
 192
 193			// If we are at the back door, remove it out of our params.
 194			if ($this->atBackDoor) {
 195				unset($p[0]);
 196				$p = array_values($p);
 197			}
 198
 199			if ($this->atSideDoor) {
 200				unset($p[0]);
 201				$p = array_values($p);
 202			}
 203
 204			if ( isset($p[0]) ) {
 205				$this->_SET['action']   = ($p[0]) ? $p[0] : 'index';
 206				unset($p[0]);
 207			}
 208
 209			if ( isset($p[1]) ) {
 210				$this->_SET['method'] = ($p[1]) ? $p[1] : 'index';
 211				unset($p[1]);
 212			}
 213
 214			foreach ($p as $key => $value) {
 215				if($value == '.json' || $value == '.xml'){
 216					$_SESSION['output'] = 'json';
 217					unset($p[$key]);
 218
 219				}
 220			}
 221
 222			$this->_SET['params'] = $p;
 223		}
 224
 225		private function openDoor()
 226		{
 227			// Door #1 
 228			// If we Do not have a DB Connected & Setup; Run through the DB Setup.
 229			if( false == file_exists(DB_CFG) ){
 230				// We need to know how to connect to our db first!
 231				// This Xtra configures the Connection to the Database. 
 232				$this->_SET['action'] = 'wwwSetup';
 233				$this->_SET['method'] = 'install';
 234				$this->atSideDoor     = true;
 235				//$this->dump()
 236			} else {		
 237				// With the DB communicating, we able to Run! 
 238				// Let the Dogs Run and Bark at them: AutoRuns. 
 239				// Access, Login, Analytics, Backup, wwwSetup
 240				$this->autoRunSniff();
 241			}
 242			
 243			// The Door is Open; All the Xtras are Locked & Loaded; the Xengine is Up and Running;
 244			// Allow them to the Walk the Path Requested, ie: /login/logout )
 245			$this->walkPath();
 246		}
 247
 248		/*
 249			Here is where we loop through the autoRun methods
 250			allowing them to sniff the Request and do their own Magic.
 251		*/ 
 252
 253		private function autoRunSniff(){
 254			// autoRun the  requisets.
 255			$this->_comment("Running AutoRun Xtra's");
 256			//	$this->dump($this->q());
 257			$q = $this->q();
 258
 259			// ok... we have a db file. but do we have a db!?
 260			$this->Q("SHOW TABLES LIKE '".$this->Q->db['prefix']."configs'");
 261
 262
 263
 264    		if($q->ERROR){
 265    			// Log the Error to The Trac System!!!!
 266				$sql = '';
 267
 268				if(isset( $this->Q->sql )){
 269					$sql = $this->removeLocalData($this->lang( 
 270						$this->_LANG['DB']['ERROR']['SQL'],
 271						array('sql' => $this->Q->sql)
 272					),$this->Q);
 273				}
 274
 275				$description = '';
 276				$description .= $sql;	
 277
 278				$error = array(
 279					'summary'     => 'DB ERROR :: '.$this->removeLocalData($this->Q->ERROR,$this->Q), 
 280					'description' => $description,
 281					'attr' => array(
 282						'type'      => 'defect',
 283						//'component' => 'x'.ucfirst($this->_SET['action']),
 284						'priority'  => 'trivial',
 285						'reporter'  => $_SESSION['user']['username'].'@'.$_SERVER['HTTP_HOST'],
 286						'keywords'  =>  $this->_SET['action'].'::'. $this->_SET['method']
 287					)
 288    			);
 289
 290				$this->reportSystemError($error);
 291
 292				$this->set(array(
 293					'action' => 'access',
 294					'method' => 'db',
 295					'params' => array( $this->uri, $q->ERROR )
 296				));
 297    		}else{
 298		    	if(isset($_GET['theme'])){
 299		    		$this->STYLE = $_GET['theme'];
 300		    	}
 301
 302				$xphp = $this->getXtras();
 303				
 304    			foreach($xphp as $k => $x){
 305    				try {	    			
 306	    				$class = $x['class'];		
 307						$methods = get_class_methods($class);
 308
 309    					if(in_array('autoRun', $methods)){	 
 310							$this->_comment("Auto Run: ". $class);	
 311							
 312							$this->_xtra = $class;
 313							// $return = $class::autoRun($this);
 314
 315							$class = new $class($this->_CFG);
 316							$class = $this->mergeO($class,$this);
 317							$return = call_user_func_array(array($class,'autoRun'), array($this));
 318
 319							$this->_comment('AutoRan '.get_class($class).': ');
 320
 321							foreach ($class as $key => $value) {
 322								$this->$key = $value;
 323							}
 324
 325
 326							if(is_array($return)){
 327								$this->_SET = $this->set($return);
 328								$this->_SET = array_merge($return,$this->_SET);
 329							}
 330
 331
 332							$this->_comment("AutoRun Found in: ".get_class($class)." ~ Complete...");
 333	    				}
 334    				} catch (Exception $e) {
 335    					$this->_comment($e->getMessage());
 336    				}
 337	    		}
 338    		}
 339		}
 340
 341		private function walkPath()
 342		{
 343			$this->_comment("Xtra: ".$this->_SET['action']." | Method:".$this->_SET['method']);
 344			// Look to see if any Xtra matches
 345			$Xtra = 'x'.ucwords($this->_SET['action']);
 346
 347			$this->_comment("Looking for Class $Xtra");
 348			$php  = XPHP_DIR."/$Xtra.php";
 349			$this->_comment("Looking for file $php");
 350			if( file_exists($php) ){
 351				$this->runXtra($Xtra,$php);
 352			}
 353		}
 354
 355		function is_class_method($type="public", $method, $class) {
 356			    $refl = new ReflectionMethod($class, $method);
 357			    switch($type) {
 358			        case "static":
 359			        	return $refl->isStatic();
 360			        break;
 361			        case "public":
 362			        	return $refl->isPublic();
 363			        break;
 364			        case "private":
 365			        	return $refl->isPrivate();
 366			        break;
 367			    }
 368			}
 369
 370		private function runXtra($Xtra,$php)
 371		{
 372			# require the dynamic file.
 373			require_once($php);
 374			# create a new class
 375
 376			$Xtra = new $Xtra($this->_CFG);
 377			$Xtra = $this->mergeO($Xtra,$this);
 378
 379			# if the method exists...
 380			if( method_exists($Xtra,$this->_SET['method']) && $this->is_class_method('public',$this->_SET['method'],$Xtra) )
 381			{
 382				// $Xtra->Q = $this->Q;
 383    			# call the function w/ params
 384    			$this->_comment("Running $php");	
 385
 386    			array_values($this->_SET['params']);
 387
 388
 389    		 
 390				$return = call_user_func_array( array($Xtra,$this->_SET['method']), $this->_SET['params'] );
 391
 392
 393				$this->_SET = $this->apply($this->_SET,$Xtra->_SET); 
 394				
 395				if(is_array($return))
 396					$this->_SET = array_merge($return,$this->_SET);
 397				
 398
 399
 400				// $this->dump($this->_SET); 
 401
 402				# We might have logged in...
 403    			$this->whoAmI($this->Key);
 404    			$this->whatAmI($this->Key);
 405
 406				if($return && $this->_SET['action'] == 'login' && $this->Key['is']['user']){
 407					//$this->_SET['action'] = (isset($this->wait['Xtra'])) ? $this->wait['Xtra'] : $this->_SET['action'] ;
 408					//$this->_SET['method'] = (isset($this->wait['method'])) ? $this->wait['method'] : $this->_SET['method'];
 409					// Run this over on more time...
 410					//$this->walkThru();
 411					// makes for a quick No-redirect ;)		
 412					//unset($_POST['password']);
 413				}
 414			# Illegal Method has been Called!
 415    		}else{
 416
 417    			// Test to see if Navi knows where to go.
 418
 419
 420    			# Kill the Engine send a 404!!
 421				$this->_SET['action']   = 'access';
 422				$this->_SET['method'] = 'notFound';
 423				$this->_SET['params']($this->_LANG['404'],$this->_LANG['404_msg']);
 424    		}
 425		}
 426
 427		private function browse() 
 428		{			
 429			$this->_comment("Sending data to Browser");			
 430			// Dislays/Outputs Data To Browser.
 431			
 432	    	$this->set('IS_USER',$this->Key['is']['user']);
 433			$this->set('IS_ADMIN',$this->Key['is']['admin']);
 434
 435			$this->display($this->Key['is']['content'], $this->_SET);
 436
 437			//$this->dump($this->_SET);
 438		//	exit;
 439
 440		}
 441
 442		private function display($type='html',$array)
 443		{ 
 444			// Choose which type of content we are displaying.
 445			//exit;
 446			ob_clean();
 447			if(isset($_REQUEST['callback'])){
 448				$callback = $_REQUEST['callback'];
 449				//start output
 450				if ($callback) {
 451				    header('Content-Type: text/javascript');
 452				    echo $callback . '(' . json_encode($array) . ');';
 453				} else {
 454				    header('Content-Type: application/x-json');
 455				    echo json_encode($array);
 456				}	
 457				exit;
 458			}else{
 459				switch ($type) {
 460					default:
 461						# HTML
 462						header('Content-Type: text/html');
 463						$this->viewTemplate();
 464					break;
 465
 466					case 'json': 
 467						ob_clean();	
 468						unset($array['HTML']);
 469						unset($array['ICON']);
 470						unset($array['admin_menu']);
 471						unset($array['xtras']);
 472						header('Content-type: text/javascript');
 473		    			echo json_encode($array);
 474					break;
 475					
 476					case ('xml'): 
 477						ob_clean();	
 478						header('Content-Type: text/xml');
 479		    			echo $this->viewXml($array); 
 480					break;
 481				}	
 482			}
 483		}
 484
 485		private function viewTemplate(){
 486			// This handles the templating, which file to load, what theme to request, what thumbnailer to use.
 487
 488			// Pre Bootstrap...
 489			// $layout = ($this->atBackDoor) ? 'backdoor' : 'frontdoor';
 490
 491			$layout = ($this->atBackDoor) ? 'watchtower' : 'frontdoor';
 492
 493
 494
 495			$layout = ($this->atSideDoor) ? 'sidedoor' : $layout;
 496			
 497			// Regardless of Door, if there is a DB error...
 498			// @TODO This Could be caught earlier... though
 499
 500			// $this->dump($this->lang( 
 501			// 	$this->_LANG['DB']['ERROR']['SQL'],
 502			// 	array('sql' => $this->Q->mSql)
 503			// ));
 504
 505			// DATABASE ERROR
 506			if(false !== $this->Q->ERROR && file_exists(DB_CFG)){
 507				// function removeLocalData($r,$Q)
 508				// {
 509				// 	$r = str_replace($Q->db['database'].'.', '', $r);
 510				// 	$r = str_replace($Q->db['prefix'], '', $r);
 511				// 	return $r;
 512				// } 
 513
 514				$sql = '';
 515
 516				if(isset( $this->Q->sql )){
 517					$sql = $this->removeLocalData($this->lang( 
 518						$this->_LANG['DB']['ERROR']['SQL'],
 519						array('sql' => $this->Q->sql)
 520					),$this->Q);
 521				}
 522
 523				$description = '';
 524				$description .= $sql;	
 525
 526				$error = array(
 527					'summary'     => 'DB ERROR :: '.$this->removeLocalData($this->Q->ERROR,$this->Q), 
 528					'description' => $description,
 529					'attr' => array(
 530						'type'      => 'defect',
 531						'component' => 'xCore',//.ucfirst($this->_SET['action']),
 532						'priority'  => 'trivial',
 533						'reporter'  => $_SESSION['user']['username'].'@'.$_SERVER['HTTP_HOST'],
 534						'keywords'  =>  $this->_SET['action'].'::'. $this->_SET['method']
 535					)
 536    			);
 537
 538				//$this->reportSystemError($error);
 539
 540				$layout = 'sidedoor';
 541				$this->set(array(
 542					'action' => 'access', 
 543					'method' => 'db', 
 544					'request' => $this->Q->ERROR, 
 545					'reason' => $this->lang( 
 546						$this->_LANG['DB']['ERROR']['SQL'],
 547						array('sql' => $this->Q->sql)
 548					)
 549				)); 
 550			}
 551
 552			$lib = explode('/', $this->_CFG['dir']['libs']);
 553			$lib = $lib[count($lib)-1];
 554
 555
 556			// This is our last chance to change the output's HTML template.  
 557			$html_door = ($this->atBackDoor) ? $this->_CFG['html']['private'] : $this->_CFG['html']['public'];
 558
 559			// Override any all all links with custom navigation.
 560			if( isset($this->Key['heylisten']) ){
 561				// Get List of Bloxs.
 562				$this->set(array(
 563					'action' => 'index',
 564					'method' => 'index'
 565				));	 
 566
 567			}else if(!file_exists($this->_CFG['dir']['html']
 568				.'/'.$html_door
 569				.'/'.$this->_SET['action']
 570				.'/'.$this->_SET['method']
 571				.'.html')
 572			){
 573				$this->set(array(
 574					'action' => 'access',
 575					'method' => '404'
 576				));				
 577				//$this->_SET['HTML']['HEAD']['TITLE'] = "Page Template Not Found";
 578			}
 579
 580
 581			if(is_array($this->_SET['_LANG'])){
 582				$lang = array_merge_recursive($this->_LANG,$this->_SET['_LANG']); 
 583			}
 584
 585
 586			// $this->dump($lang);
 587
 588			$assign = array(
 589				'Xtra'        => $this->_SET['action'],
 590				'method'      => $this->_SET['method'],
 591				'params'      => $this->_SET['params'],
 592				'Door'        => $html_door,
 593				'toBackDoor'  => $this->_CFG['dir']['backdoor'],
 594				'toFrontDoor' => $this->_CFG['dir']['frontdoor'],
 595				'toSideDoor'  => $this->_CFG['dir']['sidedoor'],
 596				'HTTP_HOST'   => $_SERVER['HTTP_HOST'],
 597				'html_title'  => $_SERVER['HTTP_HOST'],
 598				'USER'        => $_SESSION['user'],
 599				'ERROR'       => false, // Set this to Display an Error
 600				
 601				// Sets the template variable TPL_EXISTS to make sure we have the page
 602				'LANG'        => $lang ,
 603				
 604				// KEY
 605				'masterKey' => $this->Key,
 606
 607				// Depreciate
 608				'IS_ADMIN'    => $this->_LANG,
 609				'blox'        => false,
 610				'LAYOUT'      => $layout,
 611				'LAYOUTS'     =>  $this->_CFG['html'],
 612				'thumb'       => '/'.$this->_CFG['dir']['backdoor'].'/'.$lib.'/phpThumb/phpThumb.php?f=png&q=100&'
 613
 614			);
 615
 616			$assign['TPL_EXISTS'] = file_exists(str_replace("//","/",$this->_CFG['dir']['html'].'/'.$assign['Door'].'/'.$this->_SET['action'].'/'.$this->_SET['method'].'.html'));
 617
 618			$this->_SET['HTML']['JSAN'] = file_get_contents($this->_CFG['dir']['bin'].'/js/ux/JSAN.js');
 619
 620			// Initate Smarty and Pass the assigned vars.
 621			$this->initSmarty(array_merge($assign, $this->_SET));
 622		}
 623
 624 		/**
 625	     * initSmarty() includes the smarty files and configures the new class:
 626	     * @return new Smarty() w/ cfg;
 627	     */
 628		private function initSmarty($a){
 629			# Include the Smarty Class
 630			
 631			$this->lib($this->_CFG['SMARTY_V'].'/libs/Smarty.class.php');
 632			$dir          = $_SERVER['DOCUMENT_ROOT'] .'/'. $this->_CFG['dir']['libs']."/".$this->_CFG['SMARTY_V'];
 633			
 634			// Be sure to clean again.
 635			
 636			//ob_clean();
 637			
 638			# Start the Smarty Class
 639			$this->smarty               = new Smarty();	
 640			# Configure Smarty			
 641			$this->smarty->compile_dir  = $dir."/templates_c";
 642			$this->smarty->cache_dir    = $dir."/cache";
 643			$this->smarty->config_dir   = $dir."/configs";
 644
 645
 646			
 647
 648 
 649
 650			 
 651			$this->smarty->template_dir = $this->_CFG['dir']['html'];
 652			$this->smarty->assign($a);			
 653
 654			
 655			//
 656			// if($this->_CFG['debug']['cache']  == false){
 657			// 	$this->smarty->clearAllCache();
 658			// }
 659
 660
 661			ob_clean();
 662			$this->smarty->display('index.html');
 663			//
 664			if($this->_CFG['debug']['cache']  == false){
 665				$this->smarty->clearAllCache();
 666			}
 667
 668			return $this->smarty;
 669		}
 670
 671 		
 672	    private function ob_start(){
 673	    	/** Improve buffer usage and compression */
 674			if (function_exists('ob_start')){
 675				/** Check that Zlib is not enabled by default (value should be 1-9, else it will be an empty string) */
 676				if (@ini_get('zlib.output_compression') || !function_exists('ob_gzhandler')){
 677					ob_start();
 678				}else{
 679					ob_start('ob_gzhandler');
 680				}
 681			}
 682	    }
 683
 684		/**
 685		 * The main function for converting to an XML document.
 686		 * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document.
 687		 *
 688		 * @param array $data
 689		 * @param string $rootNodeName - what you want the root node to be - defaultsto data.
 690		 * @param SimpleXMLElement $xml - should only be used recursively
 691		 * @return string XML
 692		 */
 693		private function viewXml($data, $rootNodeName = 'data', $xml=null)
 694		{
 695			// turn off compatibility mode as simple xml throws a wobbly if you don't.
 696			ini_set ('zend.ze1_compatibility_mode', 0);
 697
 698			if ($xml == null)
 699			{
 700				$xml = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$rootNodeName />");
 701			}
 702
 703			// loop through the data passed in.
 704			foreach($data as $key => $value)
 705			{
 706				// no numeric keys in our xml please!
 707				if (is_numeric($key))
 708				{
 709					// make string key...
 710					$key = "unknownNode_". (string) $key;
 711				}
 712
 713				// replace anything not alpha numeric
 714				$key = preg_replace('/[^a-z]/i', '', $key);
 715
 716				// if there is another array found recrusively call this function
 717				if (is_array($value))
 718				{
 719					$node = $xml->addChild($key);
 720					// recrusive call.
 721					$this->viewXml($value, $rootNodeName, $node);
 722				} else {
 723					// add single node.
 724	                $value = htmlentities($value);
 725					$xml->addChild($key,$value);
 726				}
 727			}
 728			// pass back as string. or simple xml object if you want!
 729			return $xml->asXML();
 730		}
 731
 732		/*
 733		 * lib($file) a simple include function to easily grab
 734		 * the location of our library files
 735		 */
 736		public function lib($file){
 737			$this->_comment(get_class($this)." Requesting Library $file");
 738			try{
 739				require_once($this->_CFG['dir']['libs'].'/'.$file);
 740			}catch(Exception $e){
 741				throw new Exception(get_class($this)." Failed to Load Library: ".$file, 1); 
 742			} 
 743		}
 744
 745
 746		////////////////////////////////
 747		////////////////////////////////
 748		////////////////////////////////
 749		public function q()
 750		{
 751			if(!isset($this->Q)){
 752				
 753				/*if(get_parent_class($this) != ''){
 754					echo get_class($this);
 755					$c = get_parent_class($this);
 756					$this->Q = parent::q();
 757				} */
 758
 759				$this->_comment("Init DB from: ". get_class($this));
 760
 761				require(DB_CFG);
 762				foreach($this->db as $key => $value){
 763					if($key == 'pass'){
 764						for($i=0;$i<count($this->mainDomain()); $i++){
 765							$value = base64_decode($value);
 766						}
 767					}
 768					$this->db[$key] = $value;
 769				}
 770
 771				$db 	= "x".$this->_CFG['db'];
 772
 773				if(!class_exists($db)){
 774					$this->_comment('Loading DB');
 775					$this->lib("x4deep/$db.php");
 776				}
 777
 778				$this->Q = new $db($this->db['host'],$this->db['user'],$this->db['pass'],$this->db['database'],$this->db['prefix']);					
 779			}else{
 780				$this->_comment("Recycled DB from: ". get_class($this));
 781				//$this->Q = parent::q();
 782			}
 783
 784			// Return the class that may have already been created...
 785			return $this->Q;
 786		}
 787
 788		function getXTras(){
 789			// This is where we should get the control panel icons.
 790
 791			if(!isset($this->_xtras)){
 792				// We should only run this once 
 793				if ($handle = opendir(XPHP_DIR)) {
 794					$time = microtime(true); 
 795					$this->_comment("Loading Xtra Files...");
 796
 797				    while (false !== ($file = readdir($handle))) {
 798				        if ($file != "." && $file != "..") {
 799				            $ext = explode(".",$file);
 800
 801				            if(strtolower($ext[count($ext)-1]) === 'php'){
 802				            	$class = str_replace('.php','',$file);
 803				            	
 804				            	require_once(XPHP_DIR.'/'.$file);
 805				            	//$this->_comment("Loaded Xtra File ".$file);
 806
 807				            	$rc = new ReflectionClass($class);
 808								$doc = $rc->getDocComment();
 809
 810								if($doc){
 811									$data =  trim(preg_replace('/\r?\n *\* */', ' ', $doc));
 812									 
 813									preg_match_all('/@([a-z]+)\s+(.*?)\s*(?=$|@[a-z]+\s)/s', $data, $matches);
 814									$info = array_combine($matches[1], $matches[2]);
 815
 816									$ext = explode('.',$file);
 817									$jig = array(
 818										'author'  => '',
 819										'class'   => $ext[0],
 820										'file'    => $file,
 821										'icon'    => '',
 822										'link'    => '',
 823										'mini'    => '',
 824										'name'    => '',
 825										'version' => 0
 826									);
 827
 828									$files[$file] = array_merge($jig,$info);
 829								}
 830				            }
 831				        }
 832				    }
 833				    closedir($handle);
 834					ksort($files);
 835					//$this->set('xphp_files',$files);
 836					$this->_xtras = $this->_SET['xtras'] = $files;
 837
 838					$time = round(microtime(true) - $time,5); 
 839					$this->_comment("Loaded ".count($files)." Xtra Files in ".$time);
 840				} 
 841			}else{
 842				$files = $this->_SET['xtras'] = $this->_xtras;
 843			}
 844			
 845			return $files;
 846		}
 847
 848		function syncDbTables(){
 849			// Go through all the modules.
 850			
 851			$mods = $this->getXTras();
 852			foreach($mods as $k => $v){
 853				$php = str_replace('.php','',$k);
 854				if( method_exists($php,'dbSync') ){
 855					$db = $php::dbSync();
 856					if(!empty($db)){
 857						foreach($db as $table => $columns){
 858							if(!empty($columns)){
 859								$this->syncTable($table,$columns);
 860							}
 861							
 862						}
 863					}
 864				}
 865			}
 866		}
 867
 868		// MySQL Function...
 869		function syncTable($table,$columns){
 870			$q = $this->q();
 871			// Get Current Table
 872
 873			// $chk = $q->Q("DESC $table");
 874
 875			// // RENAME THIS TABLE TO STANDARD PREFIX TEQ
 876			// if(!empty($chk) && $q->PREFIX){
 877			// 	$q->Q("RENAME TABLE $table TO $q->PREFIX$table");
 878			// }
 879
 880			//$c = $q->Q("DESC $q->PREFIX$table");
 881
 882			$sql = '';
 883
 884			$c = $q->Q("SHOW TABLES LIKE '$q->PREFIX$table'");
 885
 886			if(!empty($c)){
 887				$c = $q->Q("DESC $q->PREFIX$table");
 888			}
 889
 890			if(!empty($c) && is_array($columns)){
 891				foreach($c as $k => $v){
 892					$col = $v['Field'];
 893					// Check Columns
 894					if( isset( $columns[$col] ) ){
 895						// Column Doesn't Match
 896						if(is_array($columns[$col])){
 897							if($columns[$col]['Type'] != $v['Type'] || (isset($columns[$col]['Default']) && $v['Default'] != $columns[$col]['Default'])  ){
 898								// Add Sync to Sql
 899								$sync = "`$v[Field]` `$v[Field]` ".$columns[$col]['Type'];
 900
 901								if(isset($columns[$col]['Default'])){
 902									$sync .= ' DEFAULT "'.$columns[$col]['Default'].'"';
 903								}
 904
 905
 906								$sql = ($sql) ? $sql.", CHANGE $sync " : $sync;
 907							}
 908						}else if(is_string($columns[$col]) && isset($columns[$columns[$col]]['Type'])){
 909							$sync = "`$col` `$columns[$col]` ".$columns[$columns[$col]]['Type'];
 910							$sql = ($sql) ? $sql.", CHANGE $sync " : $sync;
 911						}
 912						unset( $columns[$col] );
 913						unset( $c[$k] );
 914					}
 915				}
 916
 917				// Change Columns
 918				if($sql){
 919					// Run SQL
 920					$q->Q("ALTER TABLE `$q->PREFIX$table` CHANGE $sql");
 921					if(isset($_GET['debug'])){
 922						//echo $q->mSql.'<hr>';
 923						//echo $q->error;
 924					}
 925					return $q->error;
 926				}
 927
 928				// New columns
 929				if(!empty($columns)){
 930					foreach($columns as $k => $v){
 931						if(is_array($v)){
 932							$sync = "`$k` ".$v['Type'];
 933							$q->Q("ALTER TABLE `$q->PREFIX$table` ADD $sync");
 934						}
 935						//echo $q->error;
 936					}
 937
 938				}
 939
 940
 941			}else{
 942
 943				if( is_array($columns) ){
 944				# CREATE TABLE
 945					foreach($columns as $k => $v){
 946						if(is_array($v)){
 947							$sync = "`$k` ".$v['Type'];
 948							$sql = ($sql) ? $sql.", $sync " : $sync;
 949						}
 950					}
 951					$q->Q("CREATE TABLE `$q->PREFIX$table`( id INT(6) NOT NULL AUTO_INCREMENT, PRIMARY KEY(id),$sql );");
 952					// echo $q->error;
 953				}else{
 954				# Rename Table...
 955					$chk = $q->Q("SHOW TABLES LIKE '$table'");
 956
 957					if(empty($chk)){
 958						$chk = $q->Q("SHOW TABLES LIKE '$q->PREFIX$table'");
 959						// RENAME THIS TABLE TO STANDARD PREFIX TEQ
 960						if(!empty($chk)){
 961							$q->Q("RENAME TABLE $q->PREFIX$table TO $q->PREFIX$columns");
 962						}
 963					}else{
 964						$q->Q("RENAME TABLE $table TO $q->PREFIX$columns");
 965					}
 966
 967				}
 968			}
 969		}
 970
 971		function mainDomain(){
 972			$domain = $_SERVER['HTTP_HOST'];
 973			$domain = explode('.',$domain);
 974			$i      = 0;
 975			while(count($domain) > 2){
 976				unset($domain[$i]);
 977				$i++;
 978			}
 979			$domain = implode('.',$domain);
 980			return $domain;
 981		}
 982
 983		
 984
 985		/*
 986	     */
 987
 988		 public function set($k,$v=false){
 989	    	//$this->dump($this->_SET);
 990	    	if(!is_array($k)){
 991	    		//var_dump($k); 
 992				return $this->_SET[$k] = $v;
 993	    		$class = get_class($this);
 994	    		$_SESSION[$class][$k] = $v;
 995	    	}else if(is_array($k)){
 996	    		return $this->_SET = array_merge($this->_SET,$k);
 997	    		 
 998	    	}
 999		} 
1000
1001		private function apply($array,$default){
1002			$new = array();
1003			foreach ($default as $key => $value) {
1004				// if array has new from default.
1005				if( isset( $array[$key] )){
1006					if( is_array($array[$key]) ){
1007						// we are dealing with an array, dig deeper.
1008						$new[$key] = $this->apply($array[$key],$default[$key]);
1009					} else {
1010						// ok - we've hit a value, lets set it in the new array.
1011						$new[$key] = $array[$key];
1012					} 
1013				}else{
1014					// the new array that got passed.
1015					$new[$key] = $value;
1016				}
1017			}
1018			return $new;
1019		}
1020
1021		private function mergeO($obj,$default){
1022			foreach ($default as $key => $value) {
1023				$obj->$key = $value;
1024			}
1025			return $obj;
1026		}
1027
1028		public function shutDownFunction() { 
1029			$error = error_get_last();
1030		  
1031		    $t = $error['type'];
1032
1033		    if($t === E_ERROR /*|| $t === E_WARNING*/){ 
1034				//ob_clean();
1035				function FriendlyErrorType($type){ 
1036				    switch($type){ 
1037				        case E_ERROR: // 1 // 
1038				            return 'E_ERROR'; 
1039				        case E_WARNING: // 2 // 
1040				            return 'E_WARNING'; 
1041				        case E_PARSE: // 4 // 
1042				            return 'E_PARSE'; 
1043				        case E_NOTICE: // 8 // 
1044				            return 'E_NOTICE'; 
1045				        case E_CORE_ERROR: // 16 // 
1046				            return 'E_CORE_ERROR'; 
1047				        case E_CORE_WARNING: // 32 // 
1048				            return 'E_CORE_WARNING'; 
1049				        case E_CORE_ERROR: // 64 // 
1050				            return 'E_COMPILE_ERROR'; 
1051				        case E_CORE_WARNING: // 128 // 
1052				            return 'E_COMPILE_WARNING'; 
1053				        case E_USER_ERROR: // 256 // 
1054				            return 'E_USER_ERROR'; 
1055				        case E_USER_WARNING: // 512 // 
1056				            return 'E_USER_WARNING'; 
1057				        case E_USER_NOTICE: // 1024 // 
1058				            return 'E_USER_NOTICE'; 
1059				        case E_STRICT: // 2048 // 
1060				            return 'E_STRICT'; 
1061				        case E_RECOVERABLE_ERROR: // 4096 // 
1062				            return 'E_RECOVERABLE_ERROR'; 
1063				        case E_DEPRECATED: // 8192 // 
1064				            return 'E_DEPRECATED'; 
1065				        case E_USER_DEPRECATED: // 16384 // 
1066				            return 'E_USER_DEPRECATED'; 
1067				    } 
1068				    return ""; 
1069				} 
1070
1071
1072				$file = realpath($_SERVER['DOCUMENT_ROOT']);
1073
1074				$file = str_replace($file , '', $error['file']);
1075
1076			    $e = array(
1077					'summary'     => FriendlyErrorType($error['type'])
1078						.' :: '.$error['message'].' :: '.$file.'#L'.$error['line'], 
1079					'description' => $error['message'].' :: '.'[source:trunk'.$file.'#L'.$error['line'].']',
1080					'attr'        => array(
1081						'type'      => 'defect',
1082						//'component' => 'x'.ucfirst($this->_SET['action']),
1083						'priority'  => 'trivial',
1084						'reporter'  => $_SESSION['user']['username'].'@'.$_SERVER['HTTP_HOST'],
1085						'keywords'  =>  $this->_SET['action'].'::'. $this->_SET['method']
1086					)
1087				);
1088
1089				$this->reportSystemError($e);		    	
1090		    }
1091
1092		}
1093
1094		private function removeLocalData($r,$Q)
1095			{
1096			$r = str_replace($Q->db['database'].'.', '', $r);
1097			$r = str_replace($Q->db['prefix'], '', $r);
1098			return $r;
1099		} 
1100
1101
1102		///////////////////////////// 
1103		//// Debug Functions
1104		//
1105		//
1106
1107		// This should be used sparingly, and in production only!
1108		public function _comment($msg,$clear=false){
1109			$time = round(microtime(true) - $this->_CFG['debug']['runtime'],5); 
1110
1111
1112
1113
1114			$echo = $this->_debugReport[] = ">'''[wiki:".get_class($this)."]''': ^[~~".$time."~~]^ $msg";
1115
1116
1117
1118			if($this->_CFG['debug']['on'])
1119				echo $echo;
1120
1121
1122
1123		}
1124
1125		public function devBug($var,$title,$dump = true,$halt = true)
1126		{
1127			# code...
1128			$this->_comment($title);
1129			$this->_dump($var,$dump,$halt);
1130		}
1131
1132		// Same with this ^^^
1133		public function dump($var,$dump=true,$halt = true)
1134		{
1135			if($this->_CFG['debug']['on']){
1136				if($dump){
1137					echo '<pre>';
1138					var_dump($var);
1139					echo '</pre>';
1140				} else {
1141					echo $var;
1142				}
1143			}
1144			if($halt)
1145				exit();
1146		}
1147
1148		/**
1149		 * rebuildSession();
1150		 * If accessing the site and users php session was lost.
1151		 * This will attempt to restore the session quitly using
1152		 * the browser cookies .
1153		 *
1154		 */
1155		function rebuildSession(){
1156			# If there are cookies, but there isnt a session...
1157			if( isset($_COOKIE['user']['secret']) && !isset($_SESSION['user']['secret']) ){
1158				//// Level 1 Security Check ////
1159				$u = $_COOKIE['user'];
1160				$secret = sha1(
1161					md5(
1162						$u['email'].base64_decode($u['hash']).$u['id'].$u['username']
1163					)
1164				);
1165
1166				# Check Cookie Secret before hitting the DB
1167				if( $secret === $u['secret'] ){
1168					# Passed Level 1 Authority - Allows access to find user in DB.
1169					$q = $this->q();
1170					$row = $q->Select('email,hash,id,username,user_secret,user_lastvisit','Users',array(
1171						'email'=>$u['email']
1172					));
1173
1174					//// Level 2 Security Check ////
1175					if($row[0]['user_secret'] === $secret){
1176						# Client is the Correct User.- ReBuild Session Data
1177						$this->setUser($row);
1178					}
1179				}
1180			}
1181		}
1182
1183		function setConfig($option,$value){
1184			$q = $this->q();
1185
1186			$cfg = $q->Select('*','config',array('config_option'=>$option));
1187			if(empty($cfg)){
1188				return $q->Insert('config',array(
1189					'config_option'	=> $option,
1190					'config_value'	=> $value
1191				));
1192			}else{
1193				return $q->Update('config',array(
1194					'config_value'	=> $value
1195				),array(
1196					'id'	=> $cfg[0]['id']
1197				));
1198			}
1199		}
1200
1201		// allows
1202		public function __call($method,$args){
1203
1204			if(isset($this->_xtra)){
1205				$class = $this->_xtra;
1206				if(method_exists($class,$method)){
1207					$class = new $class($this->_CFG);
1208					$class = $this->mergeO($class,$this);
1209					return call_user_func_array(array($class,$method), $args);
1210				}
1211			}
1212 
1213
1214			if(!isset($this->_xtras)){
1215				$this->getXtras();
1216			}
1217
1218			foreach($this->_xtras as $x){
1219		    	$class = $x['class'];	
1220				if(method_exists($class,$method)){
1221					$class = new $class($this->_CFG);
1222					$class = $this->mergeO($class,$this);
1223					return call_user_func_array(array($class,$method), $args);
1224				}
1225		    }
1226			
1227
1228	        // if(method_exists($x['class'],$method))
1229	        // return call_user_method_array($method,$x['class'],$args);
1230		    //throw new Exception("This Method {$method} doesn't exist in ".get_class($this));
1231		    $this->_comment("This Method {$method} doesn't exist in ".get_class($this));
1232		}
1233
1234		public function lang($str,$extract)
1235		{
1236			extract($extract);
1237
1238			// $this->dump($username);
1239			// exit();
1240
1241			return eval('return "'.$str.'";');
1242		}
1243
1244		public function initRPC($rpc=false)
1245		{
1246			if(!$rpc && isset($this->_RPC))
1247				$rpc = $this->_RPC;
1248			else 
1249				return false;
1250
1251			if(!class_exists('Zend_Loader')){
1252				$this->lib('Zend/Loader.php'); 
1253				
1254				$z = new Zend_Loader();
1255				$z->loadClass('Zend_XmlRpc_Client');
1256			}
1257
1258
1259			$c = new Zend_XmlRpc_Client("http://$rpc[user]:$rpc[pass]@$rpc[www]");
1260
1261			// Need to bypass the webdav auth
1262			$http = $c ->getHttpClient(); 
1263			$http->setAuth( $rpc['user'], $rpc['pass'], Zend_Http_Client::AUTH_BASIC );
1264			$c->setHttpClient($http);  
1265
1266			return $this->RPC = $c;
1267		}
1268
1269		function is_email ($email, $checkDNS = false) {
1270	        //      Check that $email is a valid address
1271	        //              (http://tools.ietf.org/html/rfc3696)
1272	        //              (http://tools.ietf.org/html/rfc2822)
1273	        //              (http://tools.ietf.org/html/rfc5322#section-3.4.1)
1274	        //              (http://tools.ietf.org/html/rfc5321#section-4.1.3)
1275	        //              (http://tools.ietf.org/html/rfc4291#section-2.2)
1276	        //              (http://tools.ietf.org/html/rfc1123#section-2.1)
1277
1278	        //      the upper limit on address lengths should normally be considered to be 256
1279	        //              (http://www.rfc-editor.org/errata_search.php?rfc=3696)
1280	        if (strlen($email) > 256)       return false;   //      Too long
1281
1282	        //      Contemporary email addresses consist of a "local part" separated from
1283	        //      a "domain part" (a fully-qualified domain name) by an at-sign ("@").
1284	        //              (http://tools.ietf.org/html/rfc3696#section-3)
1285	        $index = strrpos($email,'@');
1286
1287	        if ($index === false)           return false;   //      No at-sign
1288	        if ($index === 0)                       return false;   //      No local part
1289	        if ($index > 64)                        return false;   //      Local part too long
1290
1291	        $localPart              = substr($email, 0, $index);
1292	        $domain                 = substr($email, $index + 1);
1293	        $domainLength   = strlen($domain);
1294	       
1295	        if ($domainLength === 0)        return false;   //      No domain part
1296	        if ($domainLength > 255)        return false;   //      Domain part too long
1297
1298	        //      Let's check the local part for RFC compliance...
1299	        //
1300	        //      Any ASCII graphic (printing) character other than the
1301	        //      at-sign ("@"), backslash, double quote, comma, or square brackets may
1302	        //      appear without quoting.  If any of that list of excluded characters
1303	        //      are to appear, they must be quoted
1304	        //              (http://tools.ietf.org/html/rfc3696#section-3)
1305	        if (preg_match('/^"(?:.)*"$/', $localPart) > 0) {
1306	                //      Quoted-string tests:
1307	                //
1308	                //      Note that since quoted-pair
1309	                //      is allowed in a quoted-string, the quote and backslash characters may
1310	                //      appear in a quoted-string so long as they appear as a quoted-pair.
1311	                //              (http://tools.ietf.org/html/rfc2822#section-3.2.5)
1312	                $groupCount     = preg_match_all('/(?:^"|"$|\\\\\\\\|\\\\")|(\\\\|")/', $localPart, $matches);
1313	                array_multisort($matches[1], SORT_DESC);
1314	                if ($matches[1][0] !== '')                                                                              return false;   //      Unescaped quote or backslash character inside quoted string
1315	                if (preg_match('/^"\\\\*"$/', $localPart) > 0)                                  return false;   //      "" and "\" are slipping through - must tidy this up
1316	        } else {
1317	                //      Unquoted string tests:
1318	                //
1319	                //      Period (".") may...appear, but may not be used to start or end the
1320	                //      local part, nor may two or more consecutive periods appear.
1321	                //              (http://tools.ietf.org/html/rfc3696#section-3)
1322	                if (preg_match('/^\\.|\\.\\.|\\.$/', $localPart) > 0)                   return false;   //      Dots in wrong place
1323
1324	                //      Any excluded characters? i.e. <space>, @, [, ], \, ", <comma>
1325	                if (preg_match('/[ @\\[\\]\\\\",]/', $localPart) > 0)
1326	                        //      Check all excluded characters are escaped
1327	                        $stripped = preg_replace('/\\\\[ @\\[\\]\\\\",]/', '', $localPart);
1328	                        if (preg_match('/[ @\\[\\]\\\\",]/', $stripped) > 0)            return false;   //      Unquoted excluded characters
1329	        }
1330
1331	        //      Now let's check the domain part...
1332
1333	        //      The domain name can also be replaced by an IP address in square brackets
1334	        //              (http://tools.ietf.org/html/rfc3696#section-3)
1335	        //              (http://tools.ietf.org/html/rfc5321#section-4.1.3)
1336	        //              (http://tools.ietf.org/html/rfc4291#section-2.2)
1337	        if (preg_match('/^\\[(.)+]$/', $domain) === 1) {
1338	                //      It's an address-literal
1339	                $addressLiteral = substr($domain, 1, $domainLength - 2);
1340	                $matchesIP              = array();
1341	               
1342	                //      Extract IPv4 part from the end of the address-literal (if there is one)
1343	                if (preg_match('/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', $addressLiteral, $matchesIP) > 0) {
1344	                        $index = strrpos($addressLiteral, $matchesIP[0]);
1345	                       
1346	                        if ($index === 0) {
1347	                                //      Nothing there except a valid IPv4 address, so...
1348	                                return true;
1349	                        } else {
1350	                                //      Assume it's an attempt at a mixed address (IPv6 + IPv4)
1351	                                if ($addressLiteral[$index - 1] !== ':')                        return false;   //      Character preceding IPv4 address must be ':'
1352	                                if (substr($addressLiteral, 0, 5) !== 'IPv6:')          return false;   //      RFC5321 section 4.1.3
1353
1354	                                $IPv6 = substr($addressLiteral, 5, ($index ===7) ? 2 : $index - 6);
1355	                                $groupMax = 6;
1356	                        }
1357	                } else {
1358	                        //      It must be an attempt at pure IPv6
1359	                        if (substr($addressLiteral, 0, 5) !== 'IPv6:')                  return false;   //      RFC5321 section 4.1.3
1360	                        $IPv6 = substr($addressLiteral, 5);
1361	                        $groupMax = 8;
1362	                }
1363
1364	                $groupCount     = preg_match_all('/^[0-9a-fA-F]{0,4}|\\:[0-9a-fA-F]{0,4}|(.)/', $IPv6, $matchesIP);
1365	                $index          = strpos($IPv6,'::');
1366
1367	                if ($index === false) {
1368	                        //      We need exactly the right number of groups
1369	                        if ($groupCount !== $groupMax)                                                  return false;   //      RFC5321 section 4.1.3
1370	                } else {
1371	                        if ($index !== strrpos($IPv6,'::'))                                             return false;   //      More than one '::'
1372	                        $groupMax = ($index === 0 || $index === (strlen($IPv6) - 2)) ? $groupMax : $groupMax - 1;
1373	                        if ($groupCount > $groupMax)                                                    return false;   //      Too many IPv6 groups in address
1374	                }
1375
1376	                //      Check for unmatched characters
1377	                array_multisort($matchesIP
1378	[1], SORT_DESC);
1379	                if ($matchesIP[1][0] !== '')                                                            return false;   //      Illegal characters in address
1380
1381	                //      It's a valid IPv6 address, so...
1382	                return true;
1383	        } else {
1384	                //      It's a domain name...
1385
1386	                //      The syntax of a legal Internet host name was specified in RFC-952
1387	                //      One aspect of host name syntax is hereby changed: the
1388	                //      restriction on the first character is relaxed to allow either a
1389	                //      letter or a digit.
1390	                //              (http://tools.ietf.org/html/rfc1123#section-2.1)
1391	                //
1392	                //      NB RFC 1123 updates RFC 1035, but this is not currently apparent from reading RFC 1035.
1393	                //
1394	                //      Most common applications, including email and the Web, will generally not permit...escaped strings
1395	                //              (http://tools.ietf.org/html/rfc3696#section-2)
1396	                //
1397	                //      Characters outside the set of alphabetic characters, digits, and hyphen MUST NOT appear in domain name
1398	                //      labels for SMTP clients or servers
1399	                //              (http://tools.ietf.org/html/rfc5321#section-4.1.2)
1400	                //
1401	                //      RFC5321 precludes the use of a trailing dot in a domain name for SMTP purposes
1402	                //              (http://tools.ietf.org/html/rfc5321#section-4.1.2)
1403	                $matches        = array();
1404	                $groupCount     = preg_match_all('/(?:[0-9a-zA-Z][0-9a-zA-Z-]{0,61}[0-9a-zA-Z]|[a-zA-Z])(?:\\.|$)|(.)/', $domain, $matches);
1405	                $level          = count($matches[0]);
1406
1407	                if ($level == 1)                                                                                        return false;   //      Mail host can't be a TLD
1408
1409	                $TLD = $matches[0][$level - 1];
1410	                if (substr($TLD, strlen($TLD) - 1, 1) === '.')                          return false;   //      TLD can't end in a dot
1411	                if (preg_match('/^[0-9]+$/', $TLD) > 0)                                         return false;   //      TLD can't be all-numeric
1412
1413	                //      Check for unmatched characters
1414	                array_multisort($matches[1], SORT_DESC);
1415	                if ($matches[1][0] !== '')                                                                      return false;   //      Illegal characters in domain, or label longer than 63 characters
1416
1417	                //      Check DNS?
1418	                if ($checkDNS && function_exists('checkdnsrr')) {
1419	                        if (!(checkdnsrr($domain, 'A') || checkdnsrr($domain, 'MX'))) {
1420	                                                                                                                                        return false;   //      Domain doesn't actually exist
1421	                        }
1422	                }
1423
1424	                //      Eliminate all other factors, and the one which remains must be the truth.
1425	                //              (Sherlock Holmes, The Sign of Four)
1426	                return true;
1427	        }
1428		}		
1429
1430
1431	}
1432?>