Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 5 additions & 15 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ parameters:
count: 1
path: src/Database/Model.php

-
message: "#^Static property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$dispatcher \\(Illuminate\\\\Contracts\\\\Events\\\\Dispatcher\\) in isset\\(\\) is not nullable\\.$#"
count: 1
path: src/Database/Model.php

-
message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Model\\:\\:errors\\(\\)\\.$#"
count: 1
Expand Down Expand Up @@ -710,21 +715,6 @@ parameters:
count: 1
path: src/Database/TreeCollection.php

-
message: "#^Winter\\\\Storm\\\\Extension\\\\Extendable\\:\\:extendableCall\\(\\) calls parent\\:\\:__call\\(\\) but Winter\\\\Storm\\\\Extension\\\\Extendable does not extend any class\\.$#"
count: 1
path: src/Extension/Extendable.php

-
message: "#^Winter\\\\Storm\\\\Extension\\\\Extendable\\:\\:extendableGet\\(\\) calls parent\\:\\:__get\\(\\) but Winter\\\\Storm\\\\Extension\\\\Extendable does not extend any class\\.$#"
count: 1
path: src/Extension/Extendable.php

-
message: "#^Winter\\\\Storm\\\\Extension\\\\Extendable\\:\\:extendableSet\\(\\) calls parent\\:\\:__set\\(\\) but Winter\\\\Storm\\\\Extension\\\\Extendable does not extend any class\\.$#"
count: 1
path: src/Extension/Extendable.php

-
message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue\\:\\:queue\\(\\)$#"
count: 1
Expand Down
3 changes: 1 addition & 2 deletions src/Database/Model.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php namespace Winter\Storm\Database;

use Cache;
use Illuminate\Support\Facades\Cache;
use Closure;
use DateTimeInterface;
use Exception;
Expand All @@ -18,7 +18,6 @@
*
* @author Alexey Bobkov, Samuel Georges
*
* @phpstan-property \Illuminate\Contracts\Events\Dispatcher|null $dispatcher
* @method static mixed extend(callable $callback, bool $scoped = false, ?object $outerScope = null)
*/
class Model extends EloquentModel implements ModelInterface
Expand Down
90 changes: 81 additions & 9 deletions src/Extension/ExtendableTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Winter\Storm\Support\ClassLoader;
use Winter\Storm\Support\Serialization;
use Illuminate\Support\Facades\App;
use ReflectionException;

/**
* This extension trait is used when access to the underlying base class
Expand Down Expand Up @@ -386,9 +387,9 @@ public function extendableGet($name)
}
}

$parent = get_parent_class();
if ($parent !== false && method_exists($parent, '__get')) {
return parent::__get($name);
$parent = $this->extensionGetParentClass();
if ($parent !== false && $this->extensionMethodExists($parent, '__get')) {
return $this->extensionCallMethod($parent, '__get', [$name]);
}

return null;
Expand All @@ -413,9 +414,9 @@ public function extendableSet($name, $value)
/*
* This targets trait usage in particular
*/
$parent = get_parent_class();
if ($parent !== false && method_exists($parent, '__set')) {
parent::__set($name, $value);
$parent = $this->extensionGetParentClass();
if ($parent !== false && $this->extensionMethodExists($parent, '__set')) {
$this->extensionCallMethod($parent, '__set', [$name, $value]);
}

/*
Expand Down Expand Up @@ -457,9 +458,9 @@ public function extendableCall($name, $params = null)
}
}

$parent = get_parent_class();
if ($parent !== false && method_exists($parent, '__call')) {
return parent::__call($name, $params);
$parent = $this->extensionGetParentClass();
if ($parent !== false && $this->extensionMethodExists($parent, '__call')) {
return $this->extensionCallMethod($parent, '__call', [$name, $params]);
}

throw new BadMethodCallException(sprintf(
Expand Down Expand Up @@ -550,4 +551,75 @@ protected function extensionGetClassLoader(): ?ClassLoader

return self::$extendableClassLoader = App::make(ClassLoader::class);
}

/**
* Gets the parent class using reflection.
*
* The parent class must either not be the `Extendable` class, or must not be using the `ExtendableTrait` trait,
* in order to prevent infinite loops.
*
* @return ReflectionClass|false
*/
protected function extensionGetParentClass(object $instance = null)
{
// Shortcut to prevent infinite loops if the class extends Extendable.
if ($this instanceof Extendable) {
return false;
}

// Find if any parent uses the Extendable trait
if (!is_null($instance)) {
$reflector = $instance;
} else {
$reflector = new ReflectionClass($this);
}
$parent = $reflector->getParentClass();

// If there's no parent, stop here.
if ($parent === false) {
return false;
}

while (!in_array(ExtendableTrait::class, $parent->getTraitNames())) {
$parent = $parent->getParentClass();
if ($parent === false) {
break;
}
}

// If no parent uses the Extendable trait, then return the parent class
if ($parent === false) {
return $reflector->getParentClass();
}

// Otherwise, we need to loop through until we find the parent class that doesn't use the Extendable trait
return $this->extensionGetParentClass($parent);
}

/**
* Determines if the given class reflection contains the given method.
*/
protected function extensionMethodExists(ReflectionClass $class, string $methodName): bool
{
try {
$method = $class->getMethod($methodName);

if (!$method->isPublic()) {
return false;
}
} catch (ReflectionException $e) {
return false;
}

return true;
}

/**
* Calls a method through reflection.
*/
protected function extensionCallMethod(ReflectionClass $class, string $method, array $params)
{
$method = $class->getMethod($method);
return $method->invokeArgs($this, $params);
}
}
2 changes: 2 additions & 0 deletions tests/Database/UpdaterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

class UpdaterTest extends TestCase
{
protected Updater $updater;

public function setUp(): void
{
include_once __DIR__.'/../fixtures/database/SampleClass.php';
Expand Down
2 changes: 2 additions & 0 deletions tests/Foundation/ApplicationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

class ApplicationTest extends TestCase
{
protected string $basePath;

protected function setUp(): void
{
// Mock application
Expand Down
2 changes: 2 additions & 0 deletions tests/Html/BlockBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

class BlockBuilderTest extends TestCase
{
protected BlockBuilder $Block;

public function setUp(): void
{
$this->Block = new BlockBuilder();
Expand Down
2 changes: 2 additions & 0 deletions tests/Support/EventFakeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

class EventFakeTest extends TestCase
{
protected EventFake $faker;

public function setUp(): void
{
$this->faker = new EventFake(new Dispatcher);
Expand Down