Skip to content

Commit

Permalink
Merge pull request #32 from otsch/feature/invade-static
Browse files Browse the repository at this point in the history
Invade static properties and methods
  • Loading branch information
freekmurze authored May 17, 2024
2 parents cf9a192 + 60173da commit a4e4891
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 4 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,40 @@ Using `invade` you can also call private functions.
invade($myClass)->privateMethod(); // returns 'private return value'
```

Further, you can also get and set private static class properties and call private static methods. Imagine having this class:

```php
class MyClass
{
private static string $privateStaticProperty = 'privateValue';

private static function privateStaticMethod(string $string, int $int): string
{
return 'private return value ' . $string . ' ' . $int;
}
}
```

Here is how you get and set private class properties:

```php
invade(MyClass::class)->get('privateStaticProperty'); // returns 'private value'

invade(MyClass::class)->set('privateStaticProperty', 'changedValue');

invade(MyClass::class)->get('privateStaticProperty'); // returns 'changedValue'
```

And this is how you call private static methods:

```php
invade(MyClass::class)
->method('privateStaticMethod')
->call('foo', 123);

// returns 'private return value foo 123'
```

## Testing

```bash
Expand Down
49 changes: 49 additions & 0 deletions src/StaticInvader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Spatie\Invade;

use Exception;

class StaticInvader
{
private ?string $method = null;

/**
* @param class-string $className
*/
public function __construct(
public string $className,
) {
}

public function get(string $name): mixed
{
return (fn () => static::${$name})->bindTo(null, $this->className)();
}

public function set(string $name, mixed $value): void
{
(fn ($value) => static::${$name} = $value)->bindTo(null, $this->className)($value);
}

public function method(string $name): self
{
$this->method = $name;

return $this;
}

/**
* @throws Exception
*/
public function call(...$params): mixed
{
if ($this->method === null) {
throw new Exception(
'No method to be called. Use it like: invadeStatic(Foo::class)->method(\'bar\')->call()'
);
}

return (fn ($method) => static::{$method}(...$params))->bindTo(null, $this->className)($this->method);
}
}
13 changes: 9 additions & 4 deletions src/functions.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
<?php

use Spatie\Invade\Invader;
use Spatie\Invade\StaticInvader;

if (! function_exists('invade')) {
/**
* @template T of object
*
* @param T $object
* @return Invader<T>
* @param T|class-string $object
* @return Invader<T>|StaticInvader
*/
function invade(object $object): Invader
function invade(object|string $object): Invader|StaticInvader
{
return new Invader($object);
if (is_object($object)) {
return new Invader($object);
}

return new StaticInvader($object);
}
}
13 changes: 13 additions & 0 deletions tests/Example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Spatie\Invade\Tests;

class Example
{
private static string $privateStaticProperty = 'privateValue';

private static function privateStaticMethod(string $string, int $int): string
{
return 'private return value ' . $string . ' ' . $int;
}
}
31 changes: 31 additions & 0 deletions tests/StaticInvaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Spatie\Invade\Tests;

beforeEach(function () {
if (invade(Example::class)->get('privateStaticProperty') === 'changedValue') {
invade(Example::class)->set('privateStaticProperty', 'privateValue');
}
});

it('reads a static private property', function () {
$privateValue = invade(Example::class)->get('privateStaticProperty');

expect($privateValue)->toBe('privateValue');
});

it('sets a private static property', function () {
invade(Example::class)->set('privateStaticProperty', 'changedValue');

$privateValue = invade(Example::class)->get('privateStaticProperty');

expect($privateValue)->toBe('changedValue');
});

it('calls a private static method', function () {
$returnValue = invade(Example::class)
->method('privateStaticMethod')
->call('test', 123);

expect($returnValue)->toBe('private return value test 123');
});

0 comments on commit a4e4891

Please sign in to comment.