Skip to content

Commit bfdcd0b

Browse files
committed
Upgrade codebase
1 parent d0bee65 commit bfdcd0b

33 files changed

+515
-524
lines changed

composer.json

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44
"description": "Design by Contract framework for PHP",
55
"license": "MIT",
66
"require": {
7-
"php": ">=7.1.3",
8-
"symfony/polyfill-php80": "^1.20",
9-
"nikic/php-parser": "^4.10"
7+
"php": "^8.1",
8+
"nikic/php-parser": "^4.13"
109
},
1110
"require-dev": {
12-
"vimeo/psalm": "^4.1",
11+
"vimeo/psalm": "^4.22",
1312
"beberlei/assert": "^3.3",
14-
"symfony/var-dumper": "^5.1",
15-
"doctrine/annotations": "^1.11",
13+
"symfony/var-dumper": "^5.4|^6.0",
1614
"roave/security-advisories": "dev-master@dev"
1715
},
1816
"autoload": {
@@ -37,12 +35,11 @@
3735
]
3836
},
3937
"suggest": {
40-
"doctrine/annotations": "^1.10 PHP 7.4 annotations support",
4138
"beberlei/assert": "^3.0 Thin assertion library for input validation in business models"
4239
},
4340
"config": {
4441
"sort-packages": true
4542
},
4643
"minimum-stability": "dev",
4744
"prefer-stable": true
48-
}
45+
}

demo/Account.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
class Account
2020
{
2121
/**
22-
* @var int
22+
* @var positive-int|0
2323
*/
24-
protected $balance = 0;
24+
protected int $balance = 0;
2525

2626
/**
2727
* Deposits fixed amount of money to the account.
@@ -38,11 +38,10 @@ public function deposit(int $amount): void
3838
/**
3939
* Withdraw amount of money from account.
4040
*
41-
* @param int $amount
41+
* @param positive-int $amount
4242
*/
4343
#[Verify('$amount <= $this->balance')]
4444
#[Verify('$amount > 0')]
45-
#[Verify('$amount <= 50')]
4645
#[Ensure('$this->balance === $old->balance - $amount')]
4746
public function withdraw(int $amount): void
4847
{
@@ -52,11 +51,11 @@ public function withdraw(int $amount): void
5251
/**
5352
* Returns current balance.
5453
*
55-
* @return positive-int
54+
* @return positive-int|0
5655
*/
5756
#[Ensure('$result === $this->balance')]
5857
public function getBalance(): int
5958
{
6059
return $this->balance;
6160
}
62-
}
61+
}

demo/demo.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
Runtime::listen('Serafim\Contracts\Demo');
1919

2020
$account = new Account();
21-
$account->deposit(-42);
21+
$account->withdraw(42);

psalm.xml

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
<?xml version="1.0"?>
22
<psalm
3+
errorLevel="1"
4+
resolveFromConfigFile="true"
35
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
46
xmlns="https://getpsalm.org/schema/config"
57
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
6-
7-
errorLevel="8"
8-
hoistConstants="true"
9-
resolveFromConfigFile="true"
10-
allowPhpStormGenerics="true"
11-
findUnusedPsalmSuppress="true"
12-
findUnusedVariablesAndParams="true"
13-
ensureArrayStringOffsetsExist="true"
14-
addParamDefaultToDocblockType="true"
158
>
169
<projectFiles>
1710
<directory name="src" />

src/Attribute/Ensure.php

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,9 @@
2323
* exit, when the method exits normally, of the and throw a
2424
* {@see PostconditionException} when they are violated. Postconditions are not
2525
* checked when the method exits by throwing an exception.
26-
*
27-
* @Annotation
28-
* @Target({ "METHOD" })
2926
*/
3027
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
31-
class Ensure extends Contract
28+
final class Ensure extends Contract
3229
{
3330
/**
3431
* The postcondition that must be met by the annotated method. The
@@ -37,28 +34,23 @@ class Ensure extends Contract
3734
* may need to refer to the old value of an expression and the value
3835
* returned by the annotated method, the following extensions are allowed:
3936
*
40-
* The keyword "$result" refers to the value returned from the method, if
41-
* any. It is an error to have a method parameter named "$result".
37+
* The keyword {@see $result} refers to the value returned from the method,
38+
* if any. It is an error to have a method parameter named {@see $result}.
4239
*
43-
* @var string
44-
*/
45-
public $value;
46-
47-
/**
48-
* @param string $value
40+
* @psalm-taint-sink eval $expr
41+
* @param non-empty-string $expr
4942
*/
5043
public function __construct(
5144
#[Language('PHP')]
52-
string $value
45+
public readonly string $expr
5346
) {
54-
$this->value = $value;
5547
}
5648

5749
/**
5850
* {@inheritDoc}
5951
*/
6052
public function __toString(): string
6153
{
62-
return $this->value;
54+
return $this->expr;
6355
}
64-
}
56+
}

src/Attribute/Invariant.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@
2121
* When checking of contracts is enabled, class invariant are checked on entry
2222
* and exit of methods, and throw a {@see InvariantException} when they are
2323
* violated.
24-
*
25-
* @Annotation
26-
* @Target({ "CLASS" })
2724
*/
2825
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
2926
final class Invariant extends Contract
@@ -33,25 +30,20 @@ final class Invariant extends Contract
3330
* expression must be valid PHP code and can reference all things visible
3431
* to the class, including private members.
3532
*
36-
* @var string
37-
*/
38-
public $value;
39-
40-
/**
41-
* @param string $value
33+
* @psalm-taint-sink eval $expr
34+
* @param non-empty-string $expr
4235
*/
4336
public function __construct(
4437
#[Language('PHP')]
45-
string $value
38+
public readonly string $expr
4639
) {
47-
$this->value = $value;
4840
}
4941

5042
/**
5143
* {@inheritDoc}
5244
*/
5345
public function __toString(): string
5446
{
55-
return $this->value;
47+
return $this->expr;
5648
}
57-
}
49+
}

src/Attribute/Verify.php

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@
2020
*
2121
* When checking of contracts is enabled, precondition are checked at method
2222
* entry and throw a {@see PreconditionException} when it is violated.
23-
*
24-
* @Annotation
25-
* @Target({ "METHOD" })
2623
*/
2724
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
28-
class Verify extends Contract
25+
final class Verify extends Contract
2926
{
3027
/**
3128
* The precondition expression that must be met by the annotated method.
@@ -36,25 +33,20 @@ class Verify extends Contract
3633
* such as private fields when the method is public, but this is considered
3734
* bad style.
3835
*
39-
* @var string
40-
*/
41-
public $value;
42-
43-
/**
44-
* @param string $value
36+
* @psalm-taint-sink eval $expr
37+
* @param non-empty-string $expr
4538
*/
4639
public function __construct(
4740
#[Language('PHP')]
48-
string $value
41+
public readonly string $expr
4942
) {
50-
$this->value = $value;
5143
}
5244

5345
/**
5446
* {@inheritDoc}
5547
*/
5648
public function __toString(): string
5749
{
58-
return $this->value;
50+
return $this->expr;
5951
}
60-
}
52+
}
Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,50 +9,54 @@
99

1010
declare(strict_types=1);
1111

12-
namespace Serafim\Contracts\Internal;
12+
namespace Serafim\Contracts\Boot;
1313

14-
use Composer\Autoload\ClassLoader;
15-
use function Composer\Autoload\includeFile;
14+
use Serafim\Contracts\Boot\Loader\ComposerLoader;
15+
use Serafim\Contracts\Boot\Loader\LoaderInterface;
16+
use Serafim\Contracts\Compiler\Compiler;
17+
use Serafim\Contracts\Runtime\Exception;
1618

17-
/**
18-
* @internal Interceptor is an internal library class, please do not use it in your code.
19-
* @psalm-internal Serafim\Contracts
20-
*/
2119
final class Interceptor
2220
{
2321
/**
24-
* @var array<string|class-string>
25-
*/
26-
private $namespaces = [];
27-
28-
/**
29-
* @var ClassLoader
22+
* @var list<non-empty-string|class-string>
3023
*/
31-
private $loader;
24+
private array $namespaces = [];
3225

3326
/**
3427
* @var Storage
3528
*/
36-
private $storage;
29+
private Storage $storage;
3730

3831
/**
3932
* @var Compiler
4033
*/
41-
private $compiler;
34+
private readonly Compiler $compiler;
4235

4336
/**
44-
* @param ClassLoader $loader
45-
* @param string|null $storage
37+
* @psalm-taint-sink file $storage
38+
* @param LoaderInterface $loader
39+
* @param non-empty-string|null $storage
4640
*/
47-
public function __construct(ClassLoader $loader, string $storage = null)
41+
public function __construct(private readonly LoaderInterface $loader, string $storage = null)
4842
{
49-
$this->loader = $loader;
50-
$this->storage = new Storage($storage ?? \sys_get_temp_dir());
5143
$this->compiler = new Compiler();
44+
$this->storage = new Storage($storage ?? \sys_get_temp_dir());
45+
}
46+
47+
/**
48+
* @psalm-taint-sink file $storage
49+
* @param non-empty-string|null $storage
50+
* @return static
51+
*/
52+
public static function fromComposer(string $storage = null): self
53+
{
54+
return new self(ComposerLoader::create(), $storage);
5255
}
5356

5457
/**
55-
* @param string $directory
58+
* @psalm-taint-sink file $directory
59+
* @param non-empty-string $directory
5660
* @return $this
5761
*/
5862
public function cache(string $directory): self
@@ -63,9 +67,10 @@ public function cache(string $directory): self
6367
}
6468

6569
/**
66-
* @param array<string|class-string> $namespaces
70+
* @param non-empty-string|class-string ...$namespaces
71+
* @return void
6772
*/
68-
public function allow(array $namespaces): void
73+
public function allow(string ...$namespaces): void
6974
{
7075
foreach ($namespaces as $namespace) {
7176
$this->namespaces[] = \trim($namespace, '\\');
@@ -74,24 +79,22 @@ public function allow(array $namespaces): void
7479

7580
/**
7681
* @return void
77-
* @psalm-suppress ArgumentTypeCoercion
7882
*/
7983
public function enable(): void
8084
{
81-
\spl_autoload_register([$this, 'loadClass'], true, true);
85+
\spl_autoload_register($this->loadClass(...), true, true);
8286
}
8387

8488
/**
8589
* @return void
86-
* @psalm-suppress UnusedFunctionCall
8790
*/
8891
public function disable(): void
8992
{
90-
\spl_autoload_unregister([$this, 'loadClass']);
93+
\spl_autoload_unregister($this->loadClass(...));
9194
}
9295

9396
/**
94-
* @param string $class
97+
* @param class-string $class
9598
* @return bool
9699
*/
97100
public function isAllowed(string $class): bool
@@ -119,32 +122,26 @@ public function loadClass(string $class): bool
119122
try {
120123
return $this->findAndInclude($class);
121124
} catch (\Throwable $e) {
122-
$class = \get_class($e);
123-
124-
$instance = new $class($e->getMessage(), $e->getCode(), $e->getPrevious());
125-
126-
throw Exception::withLocation($instance, $e->getFile(), $e->getLine());
125+
throw Exception::decorate($e);
127126
}
128127
}
129128

130129
/**
131-
* @param string $class
130+
* @param class-string $class
132131
* @return bool
133132
*/
134133
private function findAndInclude(string $class): bool
135134
{
136-
$file = $this->loader->findFile($class);
135+
$file = $this->loader->getPathname($class);
137136

138137
if ($file === false) {
139138
return false;
140139
}
141140

142-
$compiled = $this->storage->cached($class, $file, function () use ($file) {
141+
require $this->storage->cached($class, $file, function () use ($file) {
143142
return $this->compiler->compile($file);
144143
});
145144

146-
includeFile($compiled);
147-
148145
return true;
149146
}
150-
}
147+
}

0 commit comments

Comments
 (0)