Ostatnio zastanawiałem się jak można pomierzyć, posprawdzać niektóre fragmenty aplikacji, wpadłem na pomysł zasotosowania metod __call(), __get(), __set() i całośc wyglada tak:
<?php
/**
* Monitor
*
* @author Paweł `hwao` Halicki
* @version 1.0.0
*/
class Monitor {
/**
* Obiekt do monitorowania
*
* @var Object
*/
private $oObject = null;
/**
* Nazwa monitorowanego obiektu
*
* @var string
*/
protected $sObject = null;
/**
* Konstruktor
*
* @param Object $oObject
*/
public function __construct( $oObject ) {
$this->oObject = $oObject;
$this->sObject = get_class( $oObject );
}
/**
* Handler pobierania właściwości
*
* @param string $sKey
* @return mixed
*/
public function __get( $sKey ) {
return $this->oObject->$sKey;
}
/**
* Handler do przypisywania wartości właściwości
*
* @param string $sKey
* @param mixed $mVal
* @return mixed
*/
public function __set( $sKey, $mVal ) {
return $this->oObject->$sKey = $mVal;
}
/**
* Handler to obsługi metod
*
* @param string $sMethod
* @param array $aArgs
* @return mixed
*/
public function __call( $sMethod, $aArgs ) {
switch( count( $aArgs ) ) { case 1:
return $this->oObject->$sMethod( $aArgs[0] );
break;
case 2:
return $this->oObject->$sMethod( $aArgs[0], $aArgs[1] );
break;
default:
case 0:
return $this->oObject->$sMethod();
break;
}
return false;
}
}
?>
Na razie nic wielkiego, dajmy jakąś klasa do prze testowania np.:
<?php
/**
* Human
*
* Testowa klasa, oczywiscie można uzyć każdej innej dowolnej
*
*/
class Human {
/**
* Imię
*
* @var string
*/
private $sFirstName = '';
/**
* Nazwisko
*
* @var string
*/
private $sSurname = '';
/**
* Nick/ksywa
*
* @var string
*/
public $sNick = '';
/**
* Czy żyje
*
* @var boolen
*/
protected $bAlive = null;
/**
* Konstruktor
*
*/
public function __construct() {
$this->bAlive = false;
}
/**
* Narodziny
*
* @param string $sFirstName
* @param string $sSurname
*/
public function Born( $sFirstName, $sSurname ) {
if( $this->bAlive == false ) {
$this->bAlive = true;
$this->sFirstName = $sFirstName;
$this->sSurname = $sSurname;
}
else {
throw new Exception( 'Object already live!' );
}
}
/**
* Ustawienie "czasu" życia
*
* @param integer $iLive
* @return boolen
*/
public function setLiveTime( $iLive ) {
return true;
}
/**
*
* @return boolen
*/
public function goToHell() { // :)
$this->bAlive = false;
return true;
}
}
?>
Nie spierajmy sie o sama budowę klasy, to tylko przykład (IMG:
http://forum.php.pl/style_emoticons/default/smile.gif)
Robimy coś takiego:
<?php
$Human = new Monitor( new Human() );
$Human->Born( 'Paweł', 'Halicki' );
$Human->setLiveTime( 100 );
$Human->sNick = 'hwao';
$Human->setLiveTime( 50 );
$Human->sNick = 'jedi hwao';
$Human->setLiveTime( 0 );
$Human->goToHell();
$Human->sNick = 'sith hwao';
?>
Wszytko działa, tak jakbyśmy operowali na obiekcie Human, ale tak naprawdę wszystkie operacje wykonywane są na obiekcie Monitor. W praktyce oznacza to że zanim cos trafi do instancji klasy Human zostaje przeanalizowane przez obiekt Monitor. Daje nam to sporo możliwość, rozbudujmy klasę Monitor w taki oto sposób:
<?php
/**
* Rozszerzenie Monitora
*
* @author hwao
* @version 1.0.0
*/
class ProMonitor extends Monitor {
/**
* Konstuktor
*
* @param Object $oObject
*/
public function __construct( $oObject ) {
parent::__construct( $oObject );
echo 'Create new instance of object "'.$this->sObject.'"'."\n"; }
/**
* Handler pobierania właściwości
*
* @param string $sKey
* @return mixed
*/
public function __get( $sKey ) {
echo 'return '.$this->sObject.'::'.$sKey."\n"; return parent::__get( $sKey );
}
/**
* Handler do przypisywania wartości właściwości
*
* @param string $sKey
* @param mixed $mVal
* @return mixed
*/
public function __set( $sKey, $mVal ) {
echo $this->sObject.'::'.$sKey.' = '.$this->____getType
( $mVal )."\n"; return parent::__set( $sKey, $mVal );
}
/**
* Handler to obsługi metod
*
* @param string $sMethod
* @param array $aArgs
* @return mixed
*/
public function __call( $sMethod, $aArgs ) {
echo $this->sObject.'::'.$sMethod.'( '.join( ', ', $aArgs ).' );'."\n"; return parent::__call( $sMethod, $aArgs );
}
/**
* Zdobywanie wiadomości o zmiennej
*
* Pobieranie jej typu i możliwie przydatnych informacji
*
* @param mixed $mVal
* @return string
*/
protected function ____getType( $mVal ) {
case 'boolean':
return '(boolean)'.( $mVal==true ? 'true' : 'false' );
break;
case 'integer':
return '(integer)'.$mVal;
break;
case 'double':
return '(double)'.$mVal;
break;
case 'string':
return '(string)''.$mVal.'''; //'(length='.strlen($mVal).')';
break;
case 'array':
return '(array)'.$mVal.'(count='.count($mVal).')'; break;
case 'object':
return '(object)'.$mVal.'(name='.get_class($mVal).')';
break;
case 'resource':
return '(resource)'.$mVal.'(type='.get_resource_type($mVal).')';
break;
case 'null':
return '(null)Null';
break;
default:
return '(unknown)'.$mVal;
break;
}
}
}
?>
Trochę zostało dodane, głównie wyświetlanie informacji co i jak się dzieje sprawdzamy co się stanie po uruchomieniu takiego kodu:
<?php
/**
* Prosty sposób użycia
*/
$Human = new ProMonitor( new Human() );
$Human->Born( 'Paweł', 'Halicki' );
$Human->setLiveTime( 100 );
$Human->sNick = 'hwao';
$Human->setLiveTime( 50 );
$Human->sNick = 'jedi hwao';
$Human->setLiveTime( 0 );
$Human->goToHell();
$Human->sNick = 'sith hwao';
?>
Po wykonaniu skryptu naszym oczom ukaże się co się działo z obiektem Human:
Kod
Create new instance of object "Human"
Human::Born( (string)'Paweł', (string)'Halicki' );
Human::setLiveTime( (integer)100 );
Human::sNick = (string)'hwao'
Human::setLiveTime( (integer)50 );
Human::sNick = (string)'jedi hwao'
Human::setLiveTime( (integer)0 );
Human::goToHell( );
Human::sNick = (string)'sith hwao'
W przykładzie widać tylko wejście ale nic nie stoi na przeszkodzie żeby dodać inne rzeczy, np.:
- czas dostępu do metod/właściwości (jak długo co się wykonuje),
- co zwraca dane metoda/właściwości.
Wszelkie pytania mile widziane, wiem że nie jest to za bardzo zaawansowane, ale komuś może się przydać.
Kod jest fragmentem
framework'a.
Pozdrawiam