Skip to content

Commit

Permalink
ForbidDynamicProperties: minor tweaks
Browse files Browse the repository at this point in the history
Thinking this over some more...
The helper methods in this class will also be needed by the `DeprecateDynamicProperties` trait and possibly by more magic method helper traits if added in the future.
Which means that in a future commit, they will probably be moved to a `DynamicPropertiesHelpers` trait which then will be `use`d by this trait.

With that in mind, I'm:
* Changing the property and method prefix from `fdp` (Forbidden Dynamic Properties) to `dpu` (Dynamic Properties Utils).
* Removing the method and property which stored the `ReflectionProperty` instance to make the `isPropertyPublic()` and `isPropertyProtected()` methods self-contained.

N.B.: This commit should be squashed into the first commit, but I didn't want to confuse potentially ongoing reviews..
  • Loading branch information
jrfnl committed Nov 23, 2023
1 parent 24b4102 commit aeec031
Showing 1 changed file with 32 additions and 45 deletions.
77 changes: 32 additions & 45 deletions src/ForbidDynamicProperties.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,22 @@ trait ForbidDynamicProperties
*
* @var int
*/
private $fdpBacktraceLimit = 3;
private $dpuBacktraceLimit = 3;

/**
* Position in the backtrace of the frame for the call to __set().
*
* @var int
*/
private $fdpSetFrame = 1;
private $dpuSetFrame = 1;

/**
* Position in the backtrace of the frame containing the information on the context
* which triggered the call to __set().
*
* @var int
*/
private $fdpOriginFrame = 2;

/**
* Reflection instance of the current property or false if not reflection instance could be created.
*
* @var ReflectionProperty|false
*/
private $fdpReflectionProp;
private $dpuOriginFrame = 2;

/**
* Magic method which handles property setting for inaccessible and unset properties,
Expand All @@ -66,23 +59,21 @@ trait ForbidDynamicProperties
*/
public function __set($name, $value)
{
$this->fdpSetReflectionProp($name);

if ($this->fdpIsPublicProperty()) {
if ($this->dpuIsPublicProperty($name)) {
// This is an unset public property, just set it.
$this->$name = $value;
return;
}

$backtrace = $this->fdpGetBacktrace();
$selfParent = $this->fdpGetParentClass($backtrace);
$callOrigin = $this->fdpGetCallOrigin($backtrace);
$backtrace = $this->dpuGetBacktrace();
$selfParent = $this->dpuGetParentClass($backtrace);
$callOrigin = $this->dpuGetCallOrigin($backtrace);

/*
* Handle calls which originate from within the class hierarchy which contains the trait.
*/
if (\is_a($callOrigin, $selfParent, true)) {
if ($this->fdpIsProtectedProperty()) {
if ($this->dpuIsProtectedProperty($name)) {
$this->$name = $value;
return;
}
Expand All @@ -99,7 +90,7 @@ public function __set($name, $value)
* Property is not accessible from the current context, use Reflection to set the value,
* but make sure the set actually succeeded.
*/
if ($this->fdpSetInaccessibleProperty($callOrigin, $name, $value) === true) {
if ($this->dpuSetInaccessibleProperty($callOrigin, $name, $value) === true) {
return;
}
}
Expand All @@ -122,49 +113,45 @@ public function __set($name, $value)
}

/**
* Retrieve a ReflectionProperty instance of the current property and store it.
* Check if the current property is public.
*
* @param string $propertyName Property name.
*
* @return void
* @return bool
*/
private function fdpSetReflectionProp($propertyName)
private function dpuIsPublicProperty($propertyName)
{
try {
$this->fdpReflectionProp = new ReflectionProperty($this, $propertyName);
return (new ReflectionProperty($this, $propertyName))->isPublic();
} catch (ReflectionException $e) {
$this->fdpReflectionProp = false;
return false;
}
}

/**
* Check if the current property is public.
*
* @return bool
*/
private function fdpIsPublicProperty()
{
return $this->fdpReflectionProp !== false && $this->fdpReflectionProp->isPublic();
}

/**
* Check if the current property is protected.
*
* @param string $propertyName Property name.
*
* @return bool
*/
private function fdpIsProtectedProperty()
private function dpuIsProtectedProperty($propertyName)
{
return $this->fdpReflectionProp !== false && $this->fdpReflectionProp->isProtected();
try {
return (new ReflectionProperty($this, $propertyName))->isProtected();
} catch (ReflectionException $e) {
return false;
}
}

/**
* Retrieve a limited backtrace of the call triggering the `__set()`.
*
* @return array[]
*/
private function fdpGetBacktrace()
private function dpuGetBacktrace()
{
return \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, $this->fdpBacktraceLimit);
return \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, $this->dpuBacktraceLimit);
}

/**
Expand All @@ -174,13 +161,13 @@ private function fdpGetBacktrace()
*
* @return string Class name or an empty string if the class name could not be determined.
*/
private function fdpGetParentClass($backtrace)
private function dpuGetParentClass($backtrace)
{
if (
isset($backtrace[$this->fdpSetFrame]['class'], $backtrace[$this->fdpSetFrame]['function'])
&& $backtrace[$this->fdpSetFrame]['function'] === '__set'
isset($backtrace[$this->dpuSetFrame]['class'], $backtrace[$this->dpuSetFrame]['function'])
&& $backtrace[$this->dpuSetFrame]['function'] === '__set'
) {
for ($class = $backtrace[$this->fdpSetFrame]['class']; ($parent = get_parent_class($class)) !== false; $class = $parent);
for ($class = $backtrace[$this->dpuSetFrame]['class']; ($parent = get_parent_class($class)) !== false; $class = $parent);
return $class;
}

Expand All @@ -196,10 +183,10 @@ private function fdpGetParentClass($backtrace)
* @return string Fully qualified class name or an empty string if the class name
* could not be determined or the call was not made from a class context.
*/
private function fdpGetCallOrigin($backtrace)
private function dpuGetCallOrigin($backtrace)
{
if (isset($backtrace[$this->fdpOriginFrame]['class'])) {
return $backtrace[$this->fdpOriginFrame]['class'];
if (isset($backtrace[$this->dpuOriginFrame]['class'])) {
return $backtrace[$this->dpuOriginFrame]['class'];
}

return '';
Expand All @@ -215,7 +202,7 @@ private function fdpGetCallOrigin($backtrace)
*
* @return bool Whether the property was succesfully set.
*/
private function fdpSetInaccessibleProperty($targetClass, $propertyName, $value)
private function dpuSetInaccessibleProperty($targetClass, $propertyName, $value)
{
try {
$reflectionProp = new ReflectionProperty($targetClass, $propertyName);
Expand Down

0 comments on commit aeec031

Please sign in to comment.