Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127156 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 53AB71A00BC for ; Sat, 19 Apr 2025 16:04:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1745078516; bh=CFVuAOSZYHWKjjlW2xMpJ57PPgCgyGdia/i1yHLN3tA=; h=References:In-Reply-To:From:Date:Subject:To:From; b=n0sb516r5WRQQr/O+9ooXuMTAz0Lgewp4x2oNKssh3YxDuXsR2aT2mQCVkl03YmgL /HkmDqrTmvEGg1DgAEyfAwPN8p41QFfl/SoKkkbTHhxuzede68bOQvbybCez0T7k7s 6nyXccd9+zFvAvdGnWfO3hAQqJ16IkfK7ZjXzueTBXsQUIngw0YrEUBQlz0XkIk8KR +Sq4bKs/xhuRJr3IHXAsbHrSYMT+2DzDeI5cBdhrD7bAmQHQRgyLB1CJLjUd3SpvFj n3dI0R8v4dSfvO41gNZmarRxFqHch1yOTWPHaf7iFxWN2L6MB2+VzDdD69qxfHpraF DW4pFnZeKIM4Q== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 5AF0718007C for ; Sat, 19 Apr 2025 16:01:55 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-yw1-f180.google.com (mail-yw1-f180.google.com [209.85.128.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sat, 19 Apr 2025 16:01:45 +0000 (UTC) Received: by mail-yw1-f180.google.com with SMTP id 00721157ae682-6ff27ad48beso21775357b3.0 for ; Sat, 19 Apr 2025 09:04:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech.net; s=google; t=1745078645; x=1745683445; darn=lists.php.net; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=VNCtyduYiV+1f8hgB3/rV97TRC1nfT6af3WnHJFDbxo=; b=e1K4b2HN5OCL4frxDt5mWmG0BVTZM1QtzSlDPK9HQnYImBV2BYbHS/IaZ/um1Si3KK w+zqP+mFG7BG1bqC6C3fXyiz0wvjxX0dxaWXZXnLOsaT4Z3ikXnhh1tWwmrlowHOUgAd oPxKL7A1XYdGNIx8ThBDp7d0wC/JK+qD1Y97eA8JeT0no1+qXBBAzOBUwqnlRBIye0rO AmpvNSNVzjFI1pCZEaUB+7xDUaVDWPUwdZdsw7RdSY2GYpX0p3VfqGa11BjxCJg5oZj9 Iomug60eDqVBejgDov8nixMUNLM+SYe19GuA3pQgI9URaPGDuM16GRYsayM1Ef+3L1yQ zasw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745078645; x=1745683445; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=VNCtyduYiV+1f8hgB3/rV97TRC1nfT6af3WnHJFDbxo=; b=TSESUTbfHz6w7id8NpArBivT6qzf+ZyYN6A0tZPNPqxmT1HQJfgVDnCFTtfiny05Ea U2Z+lVMiz/Nghxtje2QhJa82Fow4kw2pZFqt5BUWBSL2XzJnhXrllbX5kYMOHi4CnbY9 Os1cSBbaqDBTBfDhPjhop6Jb5bz4uzvPVKxQCY9Gq/gAxxbCR1RBIbWaUFhG/GK0CIaA tOEW5v+SIaKiuPTOikHG9oMGOg6pJFnEner/cMxsVexRGgUZOQawhfcYcufcUkXhkmP/ HpKjkLSESAFDxDeX3EVK3pJeP064TSDqNkEodYu6MMJMHRwDxuQeSkZle7rLeD7JQELs YrRw== X-Gm-Message-State: AOJu0YyitIx8HF44825Y2hXDqDKBBxVelDIxhLUtmZzRkp5W39Plt/s1 z15HJAAMwbXuZluLsOLVOAueuYfNLnL9dKMOYXAOkifSpV7tEdl0i6EVBdcIW6dGpnLNutSDLHO ae1y6MpmpwtzluoUpkg4Y9OQ2q8v2/w7L/sZQ6rhCFE452nJQvDpGrg== X-Gm-Gg: ASbGnctN/xHhYX69bXg4f3+ENTv/VERvxEEszit8MtMudVnzaHsH1QSTUDTp9C0nazY KYI9Kdl4ks2GfZSNTATnlmXr1CJa3edrzwNT5AKgUTgXIo3Xq3XSVkcHXOpbLvf7ywBi2keif/e Y85WActeeQEE7l3p7GvdK46rEGL67z/QjjrUtrpkRbWiv2vN5VBmQ1WB0= X-Google-Smtp-Source: AGHT+IHKR9utGfjI37w0/OGcjc3JfB7P3zUw9poghgFtQroI4Yx9w7TzzoL+u7xz/SqPF5IFNR0p/CzVrjrSNmjrXgI= X-Received: by 2002:a05:690c:46c6:b0:6fb:b2de:a2c3 with SMTP id 00721157ae682-706cccf3462mr85067727b3.9.1745078644735; Sat, 19 Apr 2025 09:04:04 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Sat, 19 Apr 2025 18:03:53 +0200 X-Gm-Features: ATxdqUE6izTH05Og-23_T4i4CoKpFFtES2_qXG8kz888w-yJPi3Tuy6UzTtnQEQ Message-ID: Subject: [PHP-DEV] Re: Declaration-aware attributes To: PHP internals Content-Type: text/plain; charset="UTF-8" From: andreas@dqxtech.net (Andreas Hennings) To follow up on this idea. A less problematic solution would be to have a global function or static method similar to func_get_args(), that can only be called from within the constructor of an attribute. A function would be the most natural. But if we are afraid of name clashes with userland functions, we could introduce \Attribute::getReflector(). #[\Attribute] class MyAttribute { public \Reflector $reflector; public function __construct() { $this->reflector = \Attribute::getReflector(); } } #[MyAttribute] class C {} $reflection_class = new ReflectionClass(C::class); $reflection_attribute = $reflector->getAttributes()[0]; $attribute = ->newInstance(); assert($reflection_class === $attribute->reflector); We could go a bit further with this: We could have one method to get the ReflectionAttribute, and another to get the reflector of the symbol the attribute is attached to. We could have the ReflectionAttribute be aware of the original reflector, so that $reflection_class = new ReflectionClass(C::class); $reflection_attribute = $reflection_class->getAttributes()[0]; assert($reflection_attribute->getOriginatingReflector() === $reflection_class); and #[\Attribute(\Attribute::TARGET_CLASS)] class MyAttribute { public function __construct() { assert(\Attribute::getOriginatingReflector() instanceof \ReflectionClass); assert(\Attribute::getAttributeReflector() instanceof \ReflectionAttribute); } } But outside of that constructor scope it would fail: \Attribute::getOriginatingReflector(); // Throws RuntimeExeption, or a dedicated exception type. new MyAttribute(); // Throws RuntimeException or a dedicated exception type. The exact behavior here can be discussed. --- Andreas On Tue, 30 May 2023 at 02:48, Andreas Hennings wrote: > > Hello internals, > I am picking up an idea that was mentioned by Benjamin Eberlei in the past. > https://externals.io/message/110217#110395 > (we probably had the idea independently, but Benjamin's is the first > post where I see it mentioned in the list) > > Quite often I found myself writing attribute classes that need to fill > some default values or do some validation based on the symbol the > attribute is attached to. > E.g. a parameter attribute might require a specific type on that > parameter, or it might fill a default value based on the parameter > name. > > Currently I see two ways to do this: > 1. Do the logic in the code that reads the attribute, instead of the > attribute class. This works ok for one-off attribute classes, but it > becomes quite unflexible with attribute interfaces, where 3rd parties > can provide their own attribute class implementations. > 2. Add additional methods to the attribute class that take the symbol > reflector as a parameter, like "setReflectionMethod()", or > "setReflectionClass()". Or the method in the attribute class that > returns the values can have a reflector as a parameter. > > Both of these are somewhat limited and unpleasant. > > I want to propose a new way to do this. > Get some feedback first, then maybe an RFC. > > The idea is to mark constructor parameters of the attribute class with > a special parameter attribute, to receive the reflector. > The other arguments are then shifted to skip the "special" parameter. > > #[Attribute] > class A { > public function __construct( > public readonly string $x, > #[AttributeContextClass] > public readonly \ReflectionClass $class, > public readonly string $y, > ) {} > } > > $a = (new ReflectionClass(C::class))->getAttributes()[0]->newInstance(); > assert($a instanceof A); > assert($a->x === 'x'); > assert($a->class->getName() === 'C'); > assert($a->y === 'y'); > > Note that for methods, we typically need to know the method reflector > _and_ the class reflector, because the method could be defined in a > base class. > > #[Attribute] > class AA { > public function __construct( > #[AttributeContextClass] > public readonly \ReflectionClass $class, > #[AttributeContextMethod] > public readonly ReflectionMethod $method, > ) {} > } > > class B { > #[AA] > public function f(): void {} > } > > class CC extends B {} > > $aa = (new ReflectionMethod(CC::class, 'f))->getAttributes()[0]->newInstance(); > assert($a->class->getName() === 'CC'); > assert($a->method->getName() === 'f'); > > --- > > Notice that the original proposal by Benjamin would use an interface > and a setter method, ReflectorAwareAttribute::setReflector(). > > I prefer to use constructor parameters, because I generally prefer if > a constructor creates a complete and immutable object. > > ---- > > Thoughts? > > -- Andreas