1

Given this class:

class ValueObject
{
    public function __construct(public string $value)
    {
    }

    public function __toString(): string
    {
        return $this->value . "; this I can control";
    }
}

Instances will can be casted to bool, int, and float yet their value is a truthy 1 / true result. It should not be allowed:

$object = new ValueObject('42');
print_r([
    (int) $object,  // should throw
    (float) $object,  // should throw
    (bool) $object, // should throw, but only for this class instances, yet ok if not possible
    (string) $object, // I can make it throwable via `::__toString()`
]);

will print:

Array
(
    [0] => 1
    [1] => 1
    [2] => 1
    [3] => 42; this I can control
)

I do see warnings for float and int:

PHP Warning:  Object of class Application\ValueObject could not be converted to int 
PHP Warning:  Object of class Application\ValueObject could not be converted to float

yet on first glance the code appears as if working, yet it uses meaningless data.

How can I turn these warnings into an error?

Either for all of these int and float castings, yet better: only for this very class instances.

I can hook into the ::__toString() magic methods, yet there are no equivalents for like ::__toFloat() or ::__toInt().


I am on php 8.0.30.


I realized that this answer would work for me:

function exception_error_handler(int $errno, string $errstr, string $errfile = null, int $errline) {
    if (!(error_reporting() & $errno)) {
        // This error code is not included in error_reporting
        return;
    }
    throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler(__NAMESPACE__ . "\\exception_error_handler");

Yet I rather only have certain warnings be converted to an Error, not all due to legacy codebase and keeping the impact as low as possible.

Hence, want to throw the error only for specific instances.

2

1 Answer 1

1

I added my own error handler and check the $errstr for my ValueObject::class (this may in certain edge cases trigger false positive, yet for my usecase this suffices).

I used:

for references.

One has to keep in mind that the return value of the custom error handler is important, so one must return false there, otherwise the other warnings and errors would only be silenced.

Hence this works as wanted for php7 (look at the linked answer for more recent versions):

<?php

namespace YourNameSpace;

function exception_error_handler(int $errno, string $errstr, string $errfile = null, int $errline) {
    if (!(error_reporting() & $errno)) {
        // This error code is not included in error_reporting
        return false;
    }

    $needle = ValueObject::class;
    if (!str_contains($errstr, $needle)) {
        return false;
    }

    throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler(__NAMESPACE__ . "\\exception_error_handler");

class ValueObject
{
    public function __construct(public string $value)
    {
    }

    public function __toString(): string
    {
        return $this->value . "; this I can control";
    }
}

unlink('path_to_file_that_does_not_exist'); // normal warning behavior applies

class Allowed extends ValueObject {};

$object = new Allowed('42');
print_r([
    'works' => (int) $object,
    'works_too' => (float) $object,
]);


$object = new ValueObject('42');
print_r([
    (int) $object,  // throws
    (float) $object,  // throws, won't be reached of course
]);

One still sees the other warnings only as warnings:

PHP Warning:  unlink(path_to_file_that_does_not_exist): No such file or directory

One can can allow the casting on certain classes, as Allowed would still only issue those warnings

PHP Warning:  Object of class YourNameSpace\Allowed could not be converted to int
PHP Warning:  Object of class YourNameSpace\Allowed could not be converted to float

and ValueObject will throw:

PHP Fatal error:  Uncaught ErrorException: Object of class YourNameSpace\ValueObject could not be converted to int in ../application/ShouldNotCast.php:45
Stack trace:
#0 application/ShouldNotCast.php(45): YourNameSpace\exception_error_handler()
#1 {main}
  thrown in application/ShouldNotCast.php on line 45

Process finished with exit code 129 (interrupted by signal 1:SIGHUP)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.