Skip to content

Commit

Permalink
[ObjectOriented] Introduce ObjectOriented component
Browse files Browse the repository at this point in the history
  • Loading branch information
azjezz committed Feb 21, 2021
1 parent 2af0f62 commit 8dcfd8a
Show file tree
Hide file tree
Showing 12 changed files with 364 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Psl/Internal/Loader.php
Expand Up @@ -456,6 +456,11 @@ final class Loader
'Psl\Encoding\Base64\decode',
'Psl\Encoding\Hex\encode',
'Psl\Encoding\Hex\decode',
'Psl\ObjectOriented\class_alias',
'Psl\ObjectOriented\class_exists',
'Psl\ObjectOriented\interface_exists',
'Psl\ObjectOriented\trait_exists',
'Psl\ObjectOriented\get_class',
];

public const INTERFACES = [
Expand Down
60 changes: 60 additions & 0 deletions src/Psl/ObjectOriented/class_alias.php
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace Psl\ObjectOriented;

use function class_alias as php_class_alias;
use function function_exists as php_function_exists;

/**
* Creates an alias for a class.
*
* Example:
*
* $class_name = Collection\Vector::class;
* $alias = 'AwesomeVector';
*
* ObjectOriented\class_alias($class_name, $alias);
* => Bool(true)
*
* $vector = new $alias(['a', 'b']);
*
* Type\object($class_name)->matches($vector);
* => Bool(true)
*
* @template T
*
* @param class-string<T> $class_name
*
* @psalm-assert-if-true class-string<T> $alias
*
* @return bool Returns false if unable to create an alias.
*/
function class_alias(string $class_name, string $alias): bool
{
// This is a weird scenario.
// assuming $alias is `Psl\Str\contains`
// attempting to call `class_exists` will result in the autoloader being triggered.
// trying to find the class `Psl\Str\contains`.
// this results composer autoloader including `src/Psl/Str/contains.php` again,
// which contains the function definition, leading to a fatal error due to
// redeclaration of the function.
//
// so we have to ensure we don't call `*_exists` function in this case.
if (!php_function_exists($alias)) {
if (class_exists($alias)) {
return false;
}

if (interface_exists($alias)) {
return false;
}

if (trait_exists($alias)) {
return false;
}
}

return php_class_alias($class_name, $alias);
}
25 changes: 25 additions & 0 deletions src/Psl/ObjectOriented/class_exists.php
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Psl\ObjectOriented;

use function class_exists as php_class_exists;

/**
* Checks if the class has been defined.
*
* Example:
*
* ObjectOriented\class_exists('Psl\Collection\Vector');
* => Bool(true)
*
* ObjectOriented\class_exists('Psl\Collection\Foo');
* => Bool(false)
*
* @psalm-assert-if-true class-string $class_name
*/
function class_exists(string $class_name): bool
{
return php_class_exists($class_name);
}
28 changes: 28 additions & 0 deletions src/Psl/ObjectOriented/get_class.php
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Psl\ObjectOriented;

use function get_class as php_get_class;

/**
* Returns the name of the class of an object.
*
* Example:
*
* ObjectOriented\get_class($vector);
* => Str('Psl\Collection\Vector')
*
* @template T of object
*
* @param T $object
*
* @return class-string<T>
*
* @psalm-pure
*/
function get_class(object $object): string
{
return php_get_class($object);
}
24 changes: 24 additions & 0 deletions src/Psl/ObjectOriented/interface_exists.php
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Psl\ObjectOriented;

use function interface_exists as php_interface_exists;

/**
* Checks if the interface has been defined.
*
* Example:
*
* ObjectOriented\interface_exists('Psl\Collection\VectorInterface');
* => Bool(true)
*
* ObjectOriented\interface_exists('Psl\Collection\FooInterface');
* => Bool(false)
*
*/
function interface_exists(string $interface_name): bool
{
return php_interface_exists($interface_name);
}
27 changes: 27 additions & 0 deletions src/Psl/ObjectOriented/trait_exists.php
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Psl\ObjectOriented;

use function trait_exists as php_trait_exists;

/**
* Checks if the trait has been defined.
*
* Example:
*
* ObjectOriented\class_exists('Psl\Collection\Vector');
* => Bool(true)
*
* ObjectOriented\class_exists('Psl\Collection\Foo');
* => Bool(false)
*
*
* @psalm-assert-if-true class-string $class_name
*/
function trait_exists(string $trait_name): bool
{
// PHPs trait_exists returns null in case of an error.
return php_trait_exists($trait_name) ?? false;
}
71 changes: 71 additions & 0 deletions tests/Psl/ObjectOriented/ClassAliasTest.php
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\ObjectOriented;

use PHPUnit\Framework\TestCase;
use Psl\Collection;
use Psl\Iter;
use Psl\ObjectOriented;
use Psl\Type;

final class ClassAliasTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testClassAlias(bool $expected, object $object, string $alias): void
{
$class_name = ObjectOriented\get_class($object);

static::assertSame($expected, ObjectOriented\class_alias($class_name, $alias));

if ($expected) {
// Assert alias class exists.
static::assertTrue(ObjectOriented\class_exists($alias));

// Assert an instanceof $class_name is also an instanceof $alias
static::assertTrue(Type\object($alias)->matches($object));
}
}

public function provideData(): iterable
{
yield [
true,
new Collection\MutableVector([]),
'Psl\Tests\ObjectOriented\Collection\MutableVectorAlias'
];

yield [
true,
Iter\Iterator::create([]),
'Psl\Tests\ObjectOriented\Iter\Iterator'
];

yield [
false,
Iter\Iterator::create([]),
Collection\MutableVector::class
];

yield [
false,
Iter\Iterator::create([]),
Collection\MutableVectorInterface::class
];

yield [
false,
Iter\Iterator::create([]),
Fixture\EmptyTrait::class
];

yield [
true,
Iter\Iterator::create([]),
'Psl\Str\contains'
];
}
}
26 changes: 26 additions & 0 deletions tests/Psl/ObjectOriented/ClassExistsTest.php
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\ObjectOriented;

use PHPUnit\Framework\TestCase;
use Psl\Collection;
use Psl\ObjectOriented;

final class ClassExistsTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testClassExists(bool $expected, string $class_name): void
{
static::assertSame($expected, ObjectOriented\class_exists($class_name));
}

public function provideData(): iterable
{
yield [true, Collection\MutableVector::class];
yield [false, 'Collection\MutableVector'];
}
}
9 changes: 9 additions & 0 deletions tests/Psl/ObjectOriented/Fixture/EmptyTrait.php
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\ObjectOriented\Fixture;

trait EmptyTrait
{
}
34 changes: 34 additions & 0 deletions tests/Psl/ObjectOriented/GetClassTest.php
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\ObjectOriented;

use PHPUnit\Framework\TestCase;
use Psl\Collection;
use Psl\Iter;
use Psl\ObjectOriented;

final class GetClassTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testGetClass(string $expected, object $object): void
{
static::assertSame($expected, ObjectOriented\get_class($object));
}

public function provideData(): iterable
{
yield [
Collection\MutableVector::class,
new Collection\MutableVector([])
];

yield [
Iter\Iterator::class,
Iter\Iterator::create([]),
];
}
}
27 changes: 27 additions & 0 deletions tests/Psl/ObjectOriented/InterfaceExistsTest.php
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\ObjectOriented;

use PHPUnit\Framework\TestCase;
use Psl\Collection;
use Psl\ObjectOriented;

final class InterfaceExistsTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testInterfaceExists(bool $expected, string $interface_name): void
{
static::assertSame($expected, ObjectOriented\interface_exists($interface_name));
}

public function provideData(): iterable
{
yield [true, Collection\MutableVectorInterface::class];
yield [false, Collection\MutableVector::class];
yield [false, 'Foo\Interface'];
}
}
28 changes: 28 additions & 0 deletions tests/Psl/ObjectOriented/TraitExistsTest.php
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\ObjectOriented;

use PHPUnit\Framework\TestCase;
use Psl\Collection;
use Psl\ObjectOriented;

final class TraitExistsTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testTraitExists(bool $expected, string $trait_name): void
{
static::assertSame($expected, ObjectOriented\trait_exists($trait_name));
}

public function provideData(): iterable
{
yield [true, Fixture\EmptyTrait::class];
yield [false, Collection\MutableVectorInterface::class];
yield [false, Collection\MutableVector::class];
yield [false, 'Foo\Interface'];
}
}

0 comments on commit 8dcfd8a

Please sign in to comment.