PHP 8.4:
живее всех
живых!

Валентин Удальцов

Пых, Typhoon

Property hooks

Asymmetric visibility

                    
                        namespace Photoshop;

                        function brighten(string $color, float $amount): string
                        {
                            // ...
                        }

                        $blue = '#4F5B93';

                        echo brighten($blue, 1.2);
                    
                
                    
                        final class Color
                        {
                            public static function fromHex(string $hex): self
                            {
                                return new self($hex);
                            }

                            var string $hex;

                            private function __construct(string $hex)
                            {
                                $this->hex = $hex;
                            }

                            public function brighten(float $amount): self
                            {
                                // ...
                            }
                        }

                        $blue = Color::fromHex('#4F5B93');

                        echo $blue->brighten(1.2)->hex;
                    
                

PHP 7.4

                    
                        /**
                         * @property-read string $hex
                         */
                        final class Color
                        {
                            private string $hex;

                            public function __isset(string $name): bool
                            {
                                return $name === 'hex';
                            }

                            public function __get(string $name)
                            {
                                if ($name === 'hex') {
                                    return $this->hex;
                                }

                                throw new PropertyDoesNotExist(self::class, $name);
                            }

                            // ...
                        }
                    
                

PHP 7.4

                    
                        /**
                         * @property-read string $hex
                         */
                        final class Color
                        {
                            private string $hex;

                            public function __isset(string $name): bool {}

                            public function __get(string $name) {}

                            // ...
                        }

                        $blue = Color::fromHex('#4F5B93');

                        echo $blue->brighten(1.2)->hex;
                    
                

PHP 7.4

                    
                        final class Color
                        {
                            /**
                             * @psalm-readonly-allow-private-mutation
                             */
                            public string $hex;

                            // ...
                        }

                        $blue = Color::fromHex('#4F5B93');

                        echo $blue->brighten(1.2)->hex;
                    
                

PHP 7.4

                    
                        final class Color
                        {
                            private string $hex;

                            public function getHex(): string
                            {
                                return $this->hex;
                            }

                            // ...
                        }

                        $blue = Color::fromHex('#4F5B93');

                        echo $blue->brighten(1.2)->getHex();
                    
                

PHP 8.3

                    
                        final readonly class Color
                        {
                            private function __construct(
                                public string $hex,
                            ) {}

                            // ...
                        }

                        $blue = Color::fromHex('#4F5B93');

                        echo $blue->brighten(1.2)->hex;
                    
                

PHP 8.3

                    
                        final readonly class Color
                        {
                            private function __construct(
                                private int $red,
                                private int $green,
                                private int $blue,
                            ) {}

                            // ...
                        }

                        $blue = Color::fromHex('#4F5B93');

                        echo $blue->brighten(1.2)->hex; ❌
                    
                

PHP 8.3

                    
                        final readonly class Color
                        {
                            public string $hex;

                            private function __construct(
                                private int $red,
                                private int $green,
                                private int $blue,
                            ) {
                                $this->hex = sprintf('#%X%X%X', $red, $green, $blue);
                            }

                            // ...
                        }

                        $blue = Color::fromHex('#4F5B93');

                        echo $blue->brighten(1.2)->hex;
                    
                

PHP 8.3

                    
                        final readonly class Color
                        {
                            public string $hex;

                            private function __construct(
                                private int $red,
                                private int $green,
                                private int $blue,
                            ) {
                                $this->hex = sprintf('#%X%X%X', $red, $green, $blue);
                            }

                            // ...
                        }

                        $blue = Color::fromHex('#4F5B93');

                        echo $blue->brighten(1.2)->hex;
                    
                

PHP 8.4

                    
                        final class Color
                        {
                            private function __construct(
                                private readonly int $red,
                                private readonly int $green,
                                private readonly int $blue,
                            ) {}

                            public string $hex {
                                get => sprintf('#%X%X%X', $this->red, $this->green, $this->blue);
                            }

                            // ...
                        }

                        $blue = Color::fromHex('#4F5B93');

                        echo $blue->brighten(1.2)->hex;
                    
                

PHP 8.4

                    
                        final class Color
                        {
                            private function __construct(
                                private readonly int $red,
                                private readonly int $green,
                                private readonly int $blue,
                            ) {}

                            public string $hex {
                                get => sprintf('#%X%X%X', $this->red, $this->green, $this->blue);
                            }

                            // ...
                        }

                        $blue = Color::fromHex('#4F5B93');

                        // ❌ Fatal error: Property Photoshop\Color::$hex is read-only
                        $blue->hex = 'сломайся';
                    
                

PHP 8.3

                    
                        final readonly class PageViewed {}

                        final class PageViewsProjection
                        {
                            /**
                             * @psalm-readonly-allow-private-mutation
                             */
                            public int $views = 0;

                            public function onPageViewed(PageViewed $event): void
                            {
                                ++$this->views;
                            }
                        }
                    
                

PHP 8.3

                    
                        final class PageViewsProjection
                        {
                            /**
                             * @psalm-readonly-allow-private-mutation
                             */
                            public int $views = 0;

                            public function onPageViewed(PageViewed $event): void
                            {
                                ++$this->views;
                            }
                        }

                        $projection = new PageViewsProjection();
                        $projection->onPageViewed(new PageViewed());
                        $projection->onPageViewed(new PageViewed());
                        $projection->views += 100_000;

                        echo $projection->views; // 100_002
                    
                

PHP 8.4

                    
                        final class PageViewsProjection
                        {
                            public private(set) int $views = 0;

                            public function on(PageViewed $event): void
                            {
                                ++$this->views;
                            }
                        }

                        $projection = new PageViewsProjection();

                        // ❌ Cannot modify private(set) property from global scope
                        $projection->views += 100_000;
                    
                

PHP 8.4

                    
                        interface PageStatistics
                        {
                            public int $views { get; }
                        }

                        function controller(PageStatistics $statistics): string
                        {
                            return json_encode([
                                'views' => $statistics->views,
                            ]);
                        }
                    
                

PHP 8.4

                    
                        interface PageStatistics
                        {
                            public int $views { get; }
                        }

                        final class PageViewsProjection implements PageStatistics
                        {
                            public private(set) int $views = 0;

                            public function on(PageViewed $event): void
                            {
                                ++$this->views;
                            }
                        }
                    
                

PHP 8.4

                    
                        interface PageStatistics
                        {
                            public int $views { get; }
                        }

                        final readonly class PredeterminedPageStatistics implements PageStatistics
                        {
                            public function __construct(
                                public int $views,
                            ) {}
                        }
                    
                

Lazy Objects

                    
                        final readonly class Report
                        {
                            public function __construct(
                                public array $data,
                            ) {}
                        }

                        final class Analytics
                        {
                            public function buildReport(): Report
                            {
                                return new Report($this->loadData());
                            }

                            private function loadData(): array
                            {
                                // тяжёлые запросы
                                sleep(10);

                                return ['data'];
                            }
                        }

                        $report = new Analytics()->buildReport(); // ожидание 10 секунд

                        var_dump($report->data);
                    
                
                    
                        interface Report
                        {
                            public array $data { get; }
                        }

                        final class LazyReport implements Report
                        {
                            public function __construct(
                                private readonly Closure $loader,
                            ) {}

                            public private(set) array $data {
                                get => $this->data ??= ($this->loader)();
                                set => $value;
                            }
                        }
                    
                
                    
                        final class LazyReport implements Report
                        {
                            public function __construct(
                                private readonly Closure $loader,
                            ) {}

                            public private(set) array $data {
                                get => $this->data ??= ($this->loader)();
                                set => $value;
                            }
                        }

                        final class Analytics
                        {
                            public function buildReport(): Report
                            {
                                return new LazyReport($this->loadData(...));
                            }

                            // ...
                        }

                        $report = new Analytics()->buildReport();

                        var_dump($report->data); // ожидание 10 секунд
                    
                
                    
                        final readonly class Report
                        {
                            public function __construct(
                                public array $data,
                            ) {}
                        }

                        final class Analytics
                        {
                            public function buildReport(): Report
                            {
                                return new ReflectionClass(Report::class)->newLazyGhost(
                                    initializer: function (Report $report): void {
                                        $report->__construct($this->loadData());
                                    }
                                );
                            }

                            // ...
                        }

                        $report = new Analytics()->buildReport();

                        var_dump($report->data); // ожидание 10 секунд
                    
                
                    
                        interface Analytics
                        {
                            public function buildReport(): Report;
                        }

                        final readonly class PostgresAnalytics implements Analytics
                        {
                            public function buildReport(): Report
                            {
                                // тяжёлые запросы
                                sleep(10);

                                return new Report(['data']);
                            }
                        }

                        $report = new PostgresAnalytics()->buildReport(); // ожидание 10 секунд

                        var_dump($report->data);
                    
                
                    
                        final readonly class LazyAnalytics implements Analytics
                        {
                            public function __construct(
                                private Analytics $analytics,
                            ) {}

                            public function buildReport(): Report
                            {
                                return new ReflectionClass(Report::class)->newLazyProxy(
                                    factory: $this->analytics->buildReport(...)
                                );
                            }
                        }

                        $report = new LazyAnalytics(new PostgresAnalytics())->buildReport();

                        var_dump($report->data); // ожидание 10 секунд
                    
                
                    
                        final class Analytics
                        {
                            public function buildReport(): Report
                            {
                                return new ReflectionClass(Report::class)->newLazyGhost(
                                    initializer: function (Report $report): void {
                                        $report->__construct($this->loadData());
                                    }
                                );
                            }
                        }
                    
                
                    
                        final readonly class LazyAnalytics implements Analytics
                        {
                            public function __construct(
                                private Analytics $analytics,
                            ) {}

                            public function buildReport(): Report
                            {
                                return new ReflectionClass(Report::class)->newLazyProxy(
                                    factory: $this->analytics->buildReport(...)
                                );
                            }
                        }
                    
                

Support object type in BCMath

                    
                        namespace BCMath;

                        final readonly class Number
                        {
                            public string $value;

                            public int $scale;

                            public function __construct(string|int $num) {}

                            public function add(Number|string|int $num, ?int $scale = null): self {}

                            public function floor(): self {}

                            public function round(
                                int $precision = 0,
                                RoundingMode $mode = RoundingMode::HalfAwayFromZero,
                            ): self {}

                            public function __toString(): string {}

                            // ...
                        }
                    
                
                    
                        // value: 3.341, scale: 3
                        var_dump(
                            new Number('1.23') + new Number('2.111'),
                        );

                        // value: 3.341000, scale: 6
                        var_dump(
                            new Number('1.23')->add(new Number('2.111'), scale: 6),
                        );

                        // value: 3.3, scale: 1
                        var_dump(
                            new Number('1.23')->add(new Number('2.111'), scale: 1),
                        );
                    
                

Пора обновляться!

                    
                        composer why-not php '^8.4'
                    
                

А если пока не получится...

                    
                        composer require symfony/polyfill-php84
                    
                
  • mb_ucfirst, mb_lcfirst
  • array_find, array_find_key, array_any, array_all
  • #[Deprecated]
  • CURL_HTTP_VERSION_3, CURL_HTTP_VERSION_3ONLY

Что почитать/посмотреть