Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:113589 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 61848 invoked from network); 17 Mar 2021 12:05:59 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 17 Mar 2021 12:05:59 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id E87CD1804F6 for ; Wed, 17 Mar 2021 04:59:58 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.1 required=5.0 tests=BAYES_00,BODY_8BITS, NICE_REPLY_A,RCVD_IN_DNSWL_LOW,SPF_HELO_NONE,SPF_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from wp160.webpack.hosteurope.de (wp160.webpack.hosteurope.de [80.237.132.167]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 17 Mar 2021 04:59:57 -0700 (PDT) Received: from [2a02:8109:9d40:1d44:40b6:f5d3:c567:d46c] (helo=nas.fritz.box); authenticated by wp160.webpack.hosteurope.de running ExIM with esmtpsa (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) id 1lMUqB-0005Jc-Qm; Wed, 17 Mar 2021 12:59:55 +0100 To: internals@lists.php.net References: <2ff5377c-3718-feb1-ba20-96df47357414@mabe.berlin> <9f234d2a-7875-2871-6726-e7b8175099f3@mabe.berlin> Message-ID: <91bca16e-403d-5d10-216b-d1d6ac0bbae6@mabe.berlin> Date: Wed, 17 Mar 2021 12:59:55 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.8.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Content-Language: en-US X-bounce-key: webpack.hosteurope.de;marc@mabe.berlin;1615982398;c1e54dbd; X-HE-SMSGID: 1lMUqB-0005Jc-Qm Subject: Re: [PHP-DEV] Inconsistency in enumerations RFC From: marc@mabe.berlin (Marc) On 17.03.21 09:36, Ilija Tovilo wrote: > Hi Marc > >>>> I think it would >>>> be much clearer if there would be two different interfaces like: >>>> ``` interface IntEnum extends UnitEnum { public int $value; public >>>> static function from(int$value): static; public static function >>>> tryFrom(int$value): ?static; } >>>> interface StringEnum extends UnitEnum { public string $value; public >>>> static function from(string $value): static; public static function >>>> tryFrom(string $value): ?static; } ``` or else the above example should >>>> return a NULL or even case matching the string representation instead of >>>> TypeError as it matches the defined argument types. >>> I guess the interface would be more accurate. I'm not sure if that's a >>> huge win or just unnecessarily bloats the API. I think using >>> BackedEnum as a type hint should be relatively rare in practice. >>> Optimally we could add an associated type or generic type to >>> BackedEnum but that's currently not possible. >> >> I think it would be at least be helpful for example on mapping an >> enumeration from database to PHP as it's possible with doctrine types >> and probably also for static analyzers without the need to special case >> enumeration objects. > Can you provide a concrete example where the current interface is > inadequate? I can't imagine a situation where you'd want to accept all > integer BackedEnums but string BackedEnums. If we ever expand the > types that backed enums accept the number of interfaces will grow > linearly. One example that comes to my mind would be enumeration support as doctrine type. This is a modified and simplified example of a real world doctrine type using marc-mabe/php-enum abstract class AbstractEnumType extends Type {     /** @return class-string */     abstract public function getEnum(): string;     public function convertToDatabaseValue($phpValue, AbstractPlatform $platform): ?string {         $enum = $this->getEnum();         if (!$phpValue instanceof $enum) {             throw new RuntimeException('Unexpected $phpValue');         }         return (string)$phpValue->value;     }     // This currently requires reflection     public function convertToPHPValue($dbValue, AbstractPlatform $platform): BackedEnum {         $enum = $this->getEnum();         $refl = new ReflectionEnum($enum);         if ($refl->getBackingType()->__toString() === 'int') {             return $enum::from((int)$dbValue);         }         return $enum::from($dbValue);     }     // This would be cleaner and faster     public function convertToPHPValue($dbValue, AbstractPlatform $platform): IntEnum|StringEnum {         $enum = $this->getEnum();         if ($enum instanceof IntEnum) {             return $enum::from((int)$dbValue);         }         return $enum::from((string)$dbValue);     } } ########################## Another example would be an input filter class EnumFilter extends Laminas\Filter\AbstractFilter {     /** @var class-string */     private string $enum;     public function filter($value) {         $enum = $this->enum;         $refl = new ReflectionEnum($enum);         if ($refl->getBackingType()->__toString() === 'int') {             return $enum::tryFrom((int)$value);         }         return $enum::tryFrom((string)$value);     }     // vs     public function filter($value) {         $enum = $this->enum;         if ($enum instanceof IntEnum) {             return $enum::tryFrom((int)$value);         }         return $enum::tryFrom((string)$value);     } } ######################################## I can also think of static analyzers to detect type of from(), tryFrom() and $enum->value based on the implemented interface instead of requiring them to special case integer vs string backed enums. >> I also don't think one additional interface bloats up the API or waiting >> for generics helps as nobody knows if this will ever be a thing in PHP. >> >> Would this actually require a follow-up RFC or could this be done as a >> small implementation detail change ? > Historically, things like this have been done before if nobody > objects. But that seems unjustified at this point given the lack of > concrete reasoning. Sure, this needs a concrete and approved reason. I try my best ;) > Ilija Thanks, Marc