Skip to content

Instantly share code, notes, and snippets.

@andkirby
Last active April 1, 2016 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andkirby/66ac35d1480b322233be0ae2ce40cafd to your computer and use it in GitHub Desktop.
Save andkirby/66ac35d1480b322233be0ae2ce40cafd to your computer and use it in GitHub Desktop.
This class can wrap each method in a class with custom code.(it wasn't tested with namespaces)
<?php
/**
* Usage:
* <?php
* // target class which should be wrapped
* class Class_For_Testing extends stdClass
* {
* public static function complexParams(stdClass $a, array &$foo = null, $bar = 'baz')
* {
* echo 'This is a test1' . PHP_EOL;
* }
* public function test1()
* {
* echo 'This is a test1' . PHP_EOL;
* }
*
* public function test2()
* {
* echo 'This is a test number 2!' . PHP_EOL;
* }
* }
* //create wrapper
* $wrapper = new DebugWrapper('Class_For_Testing');
* $wrapper->setMethodInjectionCode('echo "wow! my code!\n";');
* $wrapper->setMethodInjectionCodeEnd('echo "^^^ End!\n";');
* echo $wrapper->wrap() . PHP_EOL;
* echo $wrapper->getCode() . PHP_EOL;
* $wrapper->loadClass();
* // test wrapper
* $className = $wrapper->getWrappedClassName();
* $test = new $className();
* $test->test1();
* $test->test2();
*
* //output
* Wrapped_Class_For_Testing
* class Wrapped_Class_For_Testing extends Class_For_Testing {
* static public function complexParams(stdClass $a, array &$foo = NULL, $bar = 'baz') {
* echo "wow! my code!\n";
* $result = parent::complexParams($a, $foo, $bar);
* echo "^^^ End!\n";
* return $result;
* }
*
*
* public function test1() {
* echo "wow! my code!\n";
* $result = parent::test1();
* echo "^^^ End!\n";
* return $result;
* }
*
*
* public function test2() {
* echo "wow! my code!\n";
* $result = parent::test2();
* echo "^^^ End!\n";
* return $result;
* }
*
* }
*
* wow! my code!
* This is a test1
* ^^^ End!
* wow! my code!
* This is a test number 2!
* ^^^ End!
*/
/**
* Class DebugWrapper
*/
class DebugWrapper
{
/**
* Generated code
*
* @var string
*/
protected $code = '';
/**
* Target class name
*
* @var string
*/
protected $className;
/**
* Code which will be added to each method
*
* @var string
*/
protected $methodInjectionCode = "file_put_contents('/tmp/wrapping.log', __CLASS__.\"::\".__FUNCTION__, FILE_APPEND);";
/**
* Code which will be added to each method
*
* @var string
*/
protected $methodInjectionCodeEnd = '';
/**
* Set class name
*
* @param $class
*/
public function __construct($class)
{
$this->className = $class;
}
/**
* Wrap class
*
* @return bool|string
*/
public function wrap()
{
if (!$this->isClassExists()) {
return false;
}
$this->writeOpenClass();
//methods
$reflect = new ReflectionClass($this->className);
foreach ($reflect->getMethods() as $method) {
if ($method->isPrivate() || $method->isFinal()) {
//ignore private and final methods which cannot be inherited
continue;
}
list($params, $arguments) = $this->getParamsCode($method);
$this->writeMethod(
$method->getName(),
$params,
$arguments,
$method->isProtected() ? 'protected' : 'public',
$method->isStatic()
);
}
$this->writeCloseClass();
return $this->getWrappedClassName();
}
protected function isClassExists()
{
return class_exists($this->className);
}
/**
* @return $this
*/
protected function writeOpenClass()
{
$this->code = '';
$this->code .= "class Wrapped_{$this->className} extends {$this->className} {";
return $this;
}
/**
* @param $method
* @return array
*/
protected function getParamsCode(ReflectionMethod $method)
{
$params = '';
$arguments = '';
/** @var $parameter ReflectionParameter */
foreach ($method->getParameters() as $num => $parameter) {
if (0 === strpos($parameter->getName(), '__')) {
//ignore magic methods
continue;
}
if (0 !== $num) {
$params .= ', ';
$arguments .= ', ';
}
$params .= $this->getParameterType($parameter) . ' ';
if ($parameter->isPassedByReference()) {
$params .= '&';
}
$params .= '$' . $parameter->getName();
$arguments .= '$' . $parameter->getName();
if ($parameter->isDefaultValueAvailable()) {
$params .= ' = ';
if ($parameter->isDefaultValueConstant()) {
$params .= $parameter->getDefaultValueConstantName();
} else {
$params .= var_export($parameter->getDefaultValue(), true);
}
}
}
return array($params, $arguments);
}
/**
* @param $name
* @param $params
* @param $arguments
* @param $visibility
* @param $static
* @return $this
*/
protected function writeMethod($name, $params, $arguments, $visibility, $static)
{
$this->code .= "\n";
$static = $static ? 'static ' : '';
$this->code .= " {$static}{$visibility} function $name($params) {\n";
$this->code .= ' ' . $this->getMethodInjectionCode() . "\n";
$this->code .= " \$result = parent::$name($arguments);\n";
$this->code .= ' ' . $this->getMethodInjectionCodeEnd() . "\n";
$this->code .= " return \$result;\n";
$this->code .= " }\n";
$this->code .= "\n";
return $this;
}
/**
* @return $this
*/
protected function writeCloseClass()
{
$this->code .= "}\n";
return $this;
}
public function getWrappedClassName()
{
return 'Wrapped_' . $this->className;
}
/**
* Get parameter type
*
* @param ReflectionParameter $parameter
* @return string|null
*/
protected function getParameterType(ReflectionParameter $parameter)
{
$export = ReflectionParameter::export(
array(
$parameter->getDeclaringClass()->name,
$parameter->getDeclaringFunction()->name,
),
$parameter->name,
true
);
return preg_match('/[>] ([A-z]+) /', $export, $matches)
? $matches[1] : null;
}
/**
* @return string
*/
public function getMethodInjectionCode()
{
return $this->methodInjectionCode;
}
/**
* @param string $methodInjectionCode
* @return DebugWrapper
*/
public function setMethodInjectionCode($methodInjectionCode)
{
$this->methodInjectionCode = $methodInjectionCode;
return $this;
}
/**
* @return string
*/
public function getMethodInjectionCodeEnd()
{
return $this->methodInjectionCodeEnd;
}
/**
* @param string $methodInjectionCodeEnd
* @return DebugWrapper
*/
public function setMethodInjectionCodeEnd($methodInjectionCodeEnd)
{
$this->methodInjectionCodeEnd = $methodInjectionCodeEnd;
return $this;
}
/**
* @return string|null
*/
public function getCode()
{
return $this->code;
}
/**
* @return $this
*/
public function loadClass()
{
eval($this->code);
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment