/** * Method that should be called around property * * @param FieldAccess $property Joinpoint * * @Around("access(* Example->*)") * @return mixed */ public function aroundFieldAccess(FieldAccess $property) { $type = $property->getAccessType() === FieldAccess::READ ? 'read' : 'write'; $value = $property->proceed(); echo "Calling Around Interceptor for field: ", get_class($property->getThis()), "->", $property->getField()->getName(), ", access: {$type}", ", value: ", json_encode($value), "<br>\n"; return $value; }
/** * Do the stuff you want to do before and after the * field is setted. * * @param FieldAccess $fieldWrite the joinpoint that corresponds to the field write * @return mixed the result of the field set {@link Joinpoint::proceed()}, might be intercepted by the * interceptor. */ public function set(FieldAccess $fieldWrite) { $value = $fieldWrite->proceed(); $adviceMethod = $this->adviceMethod; $adviceMethod($fieldWrite); return $value; }
/** * Advice that controls an access to the properties * * @param FieldAccess $fieldAccess Joinpoint * * @Around("access(public|protected Demo\Example\PropertyDemo->*)") * @return mixed */ public function aroundFieldAccess(FieldAccess $fieldAccess) { $value = $fieldAccess->proceed(); echo "Calling Around Interceptor for ", $fieldAccess, ", value: ", json_encode($value), PHP_EOL; // $value = 666; You can change the return value for read/write operations in advice! return $value; }
/** * Advice that controls an access to the properties * * @param FieldAccess $property Joinpoint * * @Around("access(* Demo\Example\PropertyDemo->*)") * @return mixed */ public function aroundFieldAccess(FieldAccess $property) { $type = $property->getAccessType() === FieldAccess::READ ? 'read' : 'write'; $value = $property->proceed(); echo "Calling Around Interceptor for field: ", get_class($property->getThis()), "->", $property->getField()->getName(), ", access: {$type}", ", value: ", json_encode($value), PHP_EOL; // $value = 666; You can change the return value for read/write operations in advice! return $value; }
/** * @Go\Before("access(public|protected **->*)") * * @param FieldAccess $access * * @return mixed */ public function beforePropertyAccess(FieldAccess $access) { if (FieldAccess::WRITE !== $access->getAccessType()) { return $access->proceed(); } foreach ($this->propertyWriteCheckers as $checker) { $checker($access); } return $access->proceed(); }
public function testWillNotProceedExecuteOnWriteAndCrash() { $this->fieldAccess->expects($this->never())->method('proceed')->will($this->returnValue('done')); $this->fieldAccess->expects($this->once())->method('getAccessType')->will($this->returnValue(FieldAccess::WRITE)); foreach ($this->callables as $callable) { $callable->expects($this->any())->method('__invoke')->will($this->throwException(new \Exception())); } $aspect = new PropertyWriteAspect(...$this->callables); $this->setExpectedException(\Exception::class); $aspect->beforePropertyAccess($this->fieldAccess); }
public function testWillFailOnInvalidAssignedType() { $field = new ReflectionProperty(ClassWithGenericArrayTypedProperty::class, 'property'); $this->fieldAccess->expects($this->any())->method('getAccessType')->will($this->returnValue(FieldAccess::WRITE)); $this->fieldAccess->expects($this->any())->method('getField')->will($this->returnValue($field)); $this->fieldAccess->expects($this->any())->method('getValueToSet')->will($this->returnValue('new value')); $immutablePropertyCheck = new PropertyWriteTypeChecker(); // catching the exception raised by PHPUnit by converting a fatal into an exception (in the error handler) $this->setExpectedException(\PHPUnit_Framework_Error::class); $immutablePropertyCheck->__invoke($this->fieldAccess); }
/** * Advice that controls an access to the properties * * @param FieldAccess $fieldAccess Joinpoint * * @Around("access(public|protected Demo\Example\PropertyDemo->*)") * @return mixed */ public function aroundFieldAccess(FieldAccess $fieldAccess) { $isRead = $fieldAccess->getAccessType() == FieldAccess::READ; // proceed all internal advices $fieldAccess->proceed(); if ($isRead) { // if you want to change original property value, then return it by reference $value = $fieldAccess->getValue(); } else { // if you want to change value to set, then return it by reference $value = $fieldAccess->getValueToSet(); } echo "Calling After Interceptor for ", $fieldAccess, ", value: ", json_encode($value), PHP_EOL; }
/** * Intercepts access to autowired properties and injects specified dependency * * @Around("@access(Warlock\Annotation\Autowired)") * * @param FieldAccess $joinpoint Autowiring joinpoint * * @return mixed */ public function beforeAccessingAutowiredProperty(FieldAccess $joinpoint) { $obj = $joinpoint->getThis(); $field = $joinpoint->getField(); if ($joinpoint->getAccessType() == FieldAccess::READ) { /** @var Autowired $autowired */ $autowired = $this->reader->getPropertyAnnotation($field, self::ANNOTATION_NAME); $strategy = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; if (!$autowired->required) { $strategy = ContainerInterface::NULL_ON_INVALID_REFERENCE; } $value = $this->container->get($autowired, $strategy); } else { $value = $joinpoint->proceed(); } $field->setValue($obj, $value); return $value; }
/** * @param FieldAccess $access * * @return void * * @throws \RuntimeException */ public function __invoke(FieldAccess $access) { if (!($that = $access->getThis())) { return; } if (FieldAccess::WRITE !== $access->getAccessType()) { return; } $field = $access->getField(); $field->setAccessible(true); // simplistic check - won't check for multiple assignments of "null" to a "null" valued field if (null === ($currentValue = $field->getValue($that))) { return; } if (!(new DocBlock($field))->getTagsByName('immutable')) { return; } $newValue = $access->getValueToSet(); throw new \RuntimeException(sprintf('Trying to overwrite property %s#$%s of object %s#%s with a value of type "%s".' . ' The property was already given a value of type %s', $field->getDeclaringClass()->getName(), $field->getName(), get_class($that), spl_object_hash($that), is_object($newValue) ? get_class($newValue) : gettype($newValue), is_object($currentValue) ? get_class($currentValue) : gettype($currentValue))); }
public function testRaisesExceptionWhenFieldAccessIsInvalid() { $object = new ClassWithImmutableProperty(); $field = new ReflectionProperty(ClassWithImmutableProperty::class, 'property'); $object->property = 'initialized'; $this->fieldAccess->expects($this->any())->method('getThis')->will($this->returnValue($object)); $this->fieldAccess->expects($this->any())->method('getAccessType')->will($this->returnValue(FieldAccess::WRITE)); $this->fieldAccess->expects($this->any())->method('getField')->will($this->returnValue($field)); $immutablePropertyCheck = new PropertyWriteImmutabilityChecker(); $this->setExpectedException(\RuntimeException::class); $immutablePropertyCheck->__invoke($this->fieldAccess); }
/** * @param FieldAccess $access * * @return void * * @throws \ErrorException|\Exception */ public function __invoke(FieldAccess $access) { if (FieldAccess::WRITE !== $access->getAccessType()) { return; } $that = $access->getThis(); $contextClass = $that ? get_class($that) : $access->getField()->getDeclaringClass()->getName(); $baseCheckers = [new IntegerTypeChecker(), new CallableTypeChecker(), new StringTypeChecker(), new GenericObjectTypeChecker(), new ObjectTypeChecker(), new ResourceTypeChecker(), new MixedTypeChecker(), new NullTypeChecker()]; (new ApplyTypeChecks(new TypedTraversableChecker(...$baseCheckers), ...$baseCheckers))->__invoke((new PropertyTypeFinder())->__invoke($access->getField(), $contextClass), $access->getValueToSet()); }