Skip to content

Commit 47c1d05

Browse files
committed
Add support of reason phrase
1 parent 004edc8 commit 47c1d05

File tree

4 files changed

+47
-16
lines changed

4 files changed

+47
-16
lines changed

Diff for: demo/Account.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Account
2020
/**
2121
* @var positive-int|0
2222
*/
23-
#[Invariant('$this->balance >= 0')]
23+
#[Invariant('$this->balance >= 0', 'Balance can not be less than 0')]
2424
protected int $balance = 0;
2525

2626
/**

Diff for: src/Compiler/ContractsParser.php

+21-11
Original file line numberDiff line numberDiff line change
@@ -82,35 +82,45 @@ public function invariant(string $file, Attribute $node): iterable
8282
private function statements(string $file, Attribute $node, string $attr, string $stmt): iterable
8383
{
8484
if (\count($node->args) < 1) {
85-
throw SpecificationException::badType($attr, $file, $node->getLine());
85+
throw SpecificationException::invalidExpressionType($attr, $file, $node->getLine());
8686
}
8787

88-
yield $this->extractContractExpression($file, $node->args[0], $attr, $stmt);
88+
yield $this->extractContractExpression($file, $node->args[0], $node->args[1] ?? null, $attr, $stmt);
8989
}
9090

9191
/**
9292
* @psalm-taint-sink file $file
9393
* @param non-empty-string $file
94-
* @param Node\Arg $argument
94+
* @param Node\Arg $expressionArgument
95+
* @param Node\Arg|null $reasonArgument
9596
* @param class-string $attr
9697
* @param class-string $stmt
9798
* @return Statement
9899
*/
99-
private function extractContractExpression(string $file, Node\Arg $argument, string $attr, string $stmt): Statement
100-
{
101-
$value = $argument->value;
100+
private function extractContractExpression(
101+
string $file,
102+
Node\Arg $expressionArgument,
103+
?Node\Arg $reasonArgument,
104+
string $attr,
105+
string $stmt
106+
): Statement {
107+
$exprValue = $expressionArgument->value;
108+
if (!$exprValue instanceof Node\Scalar\String_) {
109+
throw SpecificationException::invalidExpressionType($attr, $file, $exprValue->getStartLine());
110+
}
102111

103-
if (!$value instanceof Node\Scalar\String_) {
104-
throw SpecificationException::badType($attr, $file, $value->getStartLine());
112+
$reasonValue = $reasonArgument?->value;
113+
if ($reasonValue !== null && !$reasonValue instanceof Node\Scalar\String_) {
114+
throw SpecificationException::invalidReasonType($attr, $file, $exprValue->getStartLine());
105115
}
106116

107-
$expression = $this->parse($file, $value->value, $attr, $argument);
117+
$expression = $this->parse($file, $exprValue->value, $attr, $expressionArgument);
108118

109119
$traverser = new NodeTraverser();
110-
$traverser->addVisitor(new ConstReplaceVisitor($file, $value->getStartLine()));
120+
$traverser->addVisitor(new ConstReplaceVisitor($file, $exprValue->getStartLine()));
111121
$traverser->traverse([$expression]);
112122

113-
return new $stmt($expression, $value->value, $file, $argument->getLine());
123+
return new $stmt($expression, $exprValue->value, $reasonValue?->value, $file, $expressionArgument->getLine());
114124
}
115125

116126
/**

Diff for: src/Compiler/Visitor/ContractsApplicatorVisitor/Statement.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ abstract class Statement implements \Stringable
3535
*
3636
* @param Expr $ast
3737
* @param non-empty-string $expression
38+
* @param string|null $reason
3839
* @param non-empty-string $file
3940
* @param positive-int $line
4041
*/
4142
public function __construct(
4243
Expr $ast,
4344
protected readonly string $expression,
45+
protected readonly ?string $reason,
4446
protected readonly string $file,
4547
protected readonly int $line
4648
) {
@@ -71,7 +73,7 @@ protected function ternary(Expr $expr, FullyQualified $exception): Expression
7173
return new Expression(
7274
new Expr\Ternary($expr, null, new Expr\Throw_(
7375
new Expr\New_($exception, [
74-
new Arg(new String_($this->expression)),
76+
new Arg(new String_($this->reason ?: $this->expression)),
7577
new Arg(new String_($this->file)),
7678
new Arg(new LNumber($this->line)),
7779
])

Diff for: src/Exception/SpecificationException.php

+22-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ class SpecificationException extends \InvalidArgumentException implements
2222
/**
2323
* @var non-empty-string
2424
*/
25-
private const ERROR_INCOMPATIBLE_TYPE = '%s MUST contain PHP code string';
25+
private const ERROR_EXPRESSION_TYPE = 'Argument #1 of %s MUST contain PHP code string';
26+
27+
/**
28+
* @var non-empty-string
29+
*/
30+
private const ERROR_REASON_TYPE = 'Argument #2 of %s MUST contain reason phrase string';
2631

2732
/**
2833
* @param string $message
@@ -60,9 +65,23 @@ public static function create(string $message, string $file, int $line): static
6065
* @param positive-int $line
6166
* @return static
6267
*/
63-
public static function badType(string $type, string $file, int $line): static
68+
public static function invalidExpressionType(string $type, string $file, int $line): static
69+
{
70+
$message = \sprintf(self::ERROR_EXPRESSION_TYPE, $type);
71+
72+
return self::create($message, $file, $line);
73+
}
74+
75+
/**
76+
* @psalm-taint-sink file $file
77+
* @param non-empty-string $type
78+
* @param non-empty-string $file
79+
* @param positive-int $line
80+
* @return static
81+
*/
82+
public static function invalidReasonType(string $type, string $file, int $line): static
6483
{
65-
$message = \sprintf(self::ERROR_INCOMPATIBLE_TYPE, $type);
84+
$message = \sprintf(self::ERROR_REASON_TYPE, $type);
6685

6786
return self::create($message, $file, $line);
6887
}

0 commit comments

Comments
 (0)