Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:123684 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 314801A009C for ; Wed, 19 Jun 2024 18:19:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1718821266; bh=zqxrnJuMsmwNDwvtsFTgnVKSJitnDFT6/l0p+Ur58i0=; h=From:Subject:Date:In-Reply-To:Cc:To:References:From; b=D3XYPbg1GexSt4R/AYmzmqNWdwnjLUMqkVZcoG2+hKAeUDxZjWM8va4aCC3GSzQR3 u35AHH0uNKRtlnhVZ4sn1uFl/TQBDZ48tShXsSPoLra1LwrQd8JzSHSX+NAG9bro4T FSRdxjKTmJOnlgM7rNCMJrtLTjOesLGT8YCHERMptL9LVn72KpaDqrtZ7jZHMYPK/I rkj1LRbVvDpZI7vDzZuw6+VpFE3jWIZIxlU1D6BXkayYz2IjIi2DwZGZh1fNeCgH4K jPnL7gQUkvbThpYLXy9mQ66JztdELdQupAgQ+YLYy8rrwA0ciYtxEc5ZPqRsE5G8JD rJbS8W2znm8KQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 2F23A18004B for ; Wed, 19 Jun 2024 18:21:05 +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=0.8 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DMARC_MISSING,HTML_MESSAGE,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) 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 ; Wed, 19 Jun 2024 18:21:04 +0000 (UTC) Received: by mail-yw1-f180.google.com with SMTP id 00721157ae682-62a2424ecb8so117177b3.1 for ; Wed, 19 Jun 2024 11:19:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=newclarity-net.20230601.gappssmtp.com; s=20230601; t=1718821191; x=1719425991; darn=lists.php.net; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:from:to:cc:subject:date:message-id:reply-to; bh=zzt+0dsyR+wK4S6VTuHPsqBeLCgBOwBI4hfstu/8idY=; b=0fhchNFk7gZ/vGNUBSha452PlwBtHPAZcspnQgmHGXauRovDST3zFPMC4lzpk4Nd1+ ybhFri/e3xXMutF33yjf7S8hi387rzwh0MSfisENRlOU4CT8rJjyuVOArdUQHOCZc27m TKndbcXDYNVLCNKPWXhYGXahviq0WoPbjqJ/I1UGrYuPRacdpZU+KZYNyoxzKVWj4Qtb yST9gOqzp49lgeMm1GqJ2/HIgeeNuIEEdXUKJLsLC47PT3zgp9jsjxuaI/EUuJtvZ4dE WBOB8osqQLom6Yf0tjCP5+uNOumCi/k64GHlzsiIu3qqA6uhsMXaijgDoJcMWMS0zaM6 cq4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718821191; x=1719425991; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=zzt+0dsyR+wK4S6VTuHPsqBeLCgBOwBI4hfstu/8idY=; b=c+BlWcH6u2MRfHsUmImYW8MElTK55wz2ZvcbKmUluOM5OpsKITageRM7tkcTkRf9Kb dwxHTg4vJkicrOPcTMbKwkg6SDp8TsHyBnumVpmPLzZ3aI5IBVVUwwfddpiWN5afSNO3 MpWuu06WZmx8xKq8n+TkX9bzZF4YJf2y2OOMzuIQ3+3GZQWkpFBGy+G6l2pEG8CNnNYz 5f/YmBsC6IV7q1KHR9iz0Jv2WgZol7uBjyvpyjFS+QSnpvSquanKtGkq0luVSndbFyYG 0fSW6D3LmDnlsJEmujjV7MTituaiVKSkXAZiyf/mkzc8aVR/erYUSCrtGNoyaaeMd8bS VOAg== X-Gm-Message-State: AOJu0YwWkSm21hpUcOJH70YhcrQpEvu47r++iS8awUhW2zGsuV0x+itp UGTIMK9uv/rTuTW7yGXukGQqdFrjaRX92ci8HSlBbKln7WTZRMu9QJcVuTPSl5Vvj8mnnfkRz9t ewCA= X-Google-Smtp-Source: AGHT+IFarazDRHng5IE+1FSYp6ZsKvEsS8r75djUngnHpV9rqRpcRxpgPyCt3j/KMrDkwRY8m2uyyA== X-Received: by 2002:a81:c907:0:b0:61a:ed1e:ecd with SMTP id 00721157ae682-63a8fed3ac5mr29701857b3.50.1718821190529; Wed, 19 Jun 2024 11:19:50 -0700 (PDT) Received: from smtpclient.apple (c-98-252-216-111.hsd1.ga.comcast.net. [98.252.216.111]) by smtp.gmail.com with ESMTPSA id 00721157ae682-63118e98f8fsm22805747b3.58.2024.06.19.11.19.50 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 19 Jun 2024 11:19:50 -0700 (PDT) Message-ID: <65D2DE30-6E6A-457E-8955-2E098F9F9A4E@newclarity.net> Content-Type: multipart/alternative; boundary="Apple-Mail=_4C0093B9-4F20-48E5-B407-24A352A558C1" Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3696.120.41.1.8\)) Subject: Re: [PHP-DEV] [RFC] Static Constructor Date: Wed, 19 Jun 2024 14:19:48 -0400 In-Reply-To: <810DAB3B-41AF-4C91-978C-FFBCDBC411F3@gmail.com> Cc: php internals To: Erick de Azevedo Lima References: <810DAB3B-41AF-4C91-978C-FFBCDBC411F3@gmail.com> X-Mailer: Apple Mail (2.3696.120.41.1.8) From: mike@newclarity.net (Mike Schinkel) --Apple-Mail=_4C0093B9-4F20-48E5-B407-24A352A558C1 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On Jun 19, 2024, at 8:33 AM, Erick de Azevedo Lima = > wrote: >=20 > Hello everybody. >=20 > I found myself wanting this feature (that I first encountered when = programming in C#) for removing a workaround from a codebase I work from = time to time. > I searched internals and found a discussion from almost a decade ago. = That discussion did not end well, mostly because of insulting = accusations. > I then decided to do some research on this subject and found out that = it's a pretty common feature in other OOP languages. > Also, as I started studying the php-src (and missed the days when I = used to program in C in my main job), I decided to do an implementation = myself even before presenting the RFC. > The implementation link can also be found at the RFC. >=20 > You can read the RFC here: > https://wiki.php.net/rfc/static_constructor = >=20 > Regards, >=20 > Erick 1. I noticed you did not include an example for Go so I wrote one up for = you in a Go playground. Hopefully you can include Go's approach in your = RFC? > https://goplay.tools/snippet/6lvAQdye9P9 = 2. Also, in the past I made frequent use of "on_load()" methods that I = would call immediately after the class code. I always wanted a static = initializer =E2=80=94 and even discussed it with Ben Ramsay at an = after-event for Atlanta PHP meetup years ago =E2=80=94 but I never = proposed in as an RFC and cannot remember if I ever discussed here on = the list. Even though I no longer actively use or maintain the following library I = wanted to provide you with links for each of the different use-case = where I had an `on_load()` method and would have used a static = initializer had it been available in hopes they may inspire you to = enhance your RFC with new use-cases. I am also sharing for others to see = the different use-cases: - https://github.com/wplib/wplib/blob/master/wplib.php#L142 = - https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L43 = =20= - https://github.com/wplib/wplib/blob/master/modules/theme/theme.php#L14 = - = https://github.com/wplib/wplib/blob/master/modules/role-administrator/role= -administrator.php#L35 = - = https://github.com/wplib/wplib/blob/master/modules/roles/includes/class-ro= le-module-base.php#L33 = - = https://github.com/wplib/wplib/blob/master/modules/commit-reviser/commit-r= eviser.php#L13 = - = https://github.com/wplib/wplib/blob/master/modules/helpers-html/helpers-ht= ml.php#L16 = =20 3. One thing about "best practices" for static initializers. When using = a package-level variable in Go initializations and for those PHP static = classes with `on_load()` methods I found it problematic to add anything = that could fail in a reasonable use-case, and I would recommend that = this would become the advice given as a best practice for using static = initializers.=20 In my PHP examples above I do happen to assume the database is open and = exists but only because the library is for use with WordPress as a = must-load plugin, and if the database is not available my plugin will = never get loaded. In Go I never use database access code or remote API calls, or anything = that could generate an error. I instead create "initialize" methods and = call them explicitly in `main()` or some other func that `main()` calls. In PHP you can have a syntax error at runtime, but in that case it will = error as soon as you try to autoload the PHP file so I do not think = syntax errors should be real concern here, only runtime errors. 4. To elaborate more about "best practices" where I see static = initializers being especially valuable it when you want to initialize = immutable data, and especially when that data is in complex form such as = an object vs. just a simple value. Here is one such example of this = which initializes the default labels for a WordPress post-type: = https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L54-L64= = =20 5. Another use-case is registering "hooks" defined by a framework, such = as the `wp_loaded` hook in WordPress: = https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L68C28-= L68C37 = =20 6. Yes another use-case would be to make a framework more robust and to = provide guidance to a user for what they are doing wrong. Here I am = actually throwing an error when the it is clear the classes are being = initialized out-of-order: = https://github.com/wplib/wplib/blob/master/wplib.php#L157 = 7. Still another is loading related .PHP files that are required so as = not to have to rely on the autoloader. https://github.com/wplib/wplib/blob/master/wplib.php#L167 = .=20 Here I would also suggest guidance about not including any code that can = fail at load time during the normal course of execution meaning syntax = errors are okay but the loaded code should really never generate a = runtime errors.=20 8. A big use-case too is allowing static classes to register themselves = as being able to provide a service to another class defined by the = application or framework. This allows a class to self-describe its = behavior and capabilities such as how my library allows the registration = of the class as a "Helper" for the main class and how it allows = registering a class as representing a "Role" in the framework: = https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L48 = = https://github.com/wplib/wplib/blob/master/modules/role-administrator/role= -administrator.php#L37 = =20 This is much like how a pub-sub system allows others to subscribe to the = publisher and the publisher does not need to know in advance all the = subscribers to the system.=20 9. Regarding Larry's concern for test mocking, if the only things that = static initialize functions are doing is initializing immutable data, = registering hooks, pre-loading non-error generating .PHP files, and/or = registering classes for special use-cases then there is #fyi (little = or?) no need to mock them for testing purposes.=20 For developers following said guidance I do not see static initializers = as creating a challenge for testing. -Mike P.S. I think it would be great if static initializers threw a runtime = error if they attempted to do anything that could throw an error =E2=80=94= at least not without a try-catch to absorb the error =E2=80=94 but as I = cannot envision how that would be possible and still be acceptably = performant I think the only approach should be provide the community = with guidance on how and how not to use static initializers. #fwiw --Apple-Mail=_4C0093B9-4F20-48E5-B407-24A352A558C1 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8
On Jun 19, 2024, at 8:33 = AM, Erick de Azevedo Lima <ericklima.comp@gmail.com> wrote:

Hello everybody.

I found = myself wanting this feature (that I first encountered when programming = in C#) for removing a workaround from a codebase I work from time to = time.
I searched internals and found a discussion from = almost a decade ago. That discussion did not end well, mostly because of = insulting accusations.
I then decided to do some research = on this subject and found out that it's a pretty common feature in other = OOP languages.
Also, as I started studying the = php-src  (and missed the days when I used to program in C in = my main job), I decided to do an implementation myself even before = presenting the RFC.
The implementation link can also be = found at the RFC.

You can read the RFC here:
https://wiki.php.net/rfc/static_constructor

Regards,

Erick

1. I noticed you did not include an example for = Go so I wrote one up for you in a Go playground. Hopefully you can = include Go's approach in your RFC?


2. Also, in = the past I made frequent use of "on_load()" methods that I would call = immediately after the class code. I always wanted a static initializer = =E2=80=94 and even discussed it with Ben Ramsay at an after-event for = Atlanta PHP meetup years ago =E2=80=94 but I never proposed in as an RFC = and cannot remember if I ever discussed here on the list.

Even though I = no longer actively use or maintain the following library I wanted to = provide you with links for each of the different use-case where I had an = `on_load()` method and would have used a = static initializer had it been available in hopes they may = inspire you to enhance your RFC with new use-cases. I am also sharing = for others to see the different use-cases:

https://github.com/wplib/wplib/blob/master/wplib.php#L142https://github.com/wplib/wplib/blob/master/modules/posts/posts.= php#L43 
https://github.com/wplib/wplib/blob/master/modules/theme/theme.= php#L14
https://github.com/wplib/wplib/blob/master/modules/role-adminis= trator/role-administrator.php#L35
https://github.com/wplib/wplib/blob/master/modules/roles/includ= es/class-role-module-base.php#L33
https://github.com/wplib/wplib/blob/master/modules/commit-revis= er/commit-reviser.php#L13
https://github.com/wplib/wplib/blob/master/modules/helpers-html= /helpers-html.php#L16 

3. One thing about "best practices" for static = initializers. When using a package-level variable in Go initializations = and for those PHP static classes with `on_load()` methods I found it = problematic to add anything that could fail in a reasonable use-case, = and I would recommend that this would become the advice given as a best = practice for using static initializers. 

In my PHP examples above = I do happen to assume the database is open and exists but only because = the library is for use with WordPress as a must-load plugin, and if the = database is not available my plugin will never get loaded.

In Go I never = use database access code or remote API calls, or anything that could = generate an error. I instead create "initialize" methods and call them = explicitly in `main()` or some other func that `main()` calls.

In PHP you = can have a syntax error at runtime, but in that case it will error as = soon as you try to autoload the PHP file so I do not think syntax errors = should be real concern here, only runtime errors.

4. To elaborate more = about "best practices" where I see static initializers being especially = valuable it when you want to initialize immutable = data, and especially when that data is in complex form such as an object = vs. just a simple value. Here is one such example of this which = initializes the default labels for a WordPress post-type: https://github.com/wplib/wplib/blob/master/modules/posts/posts.= php#L54-L64 

5. Another use-case is registering "hooks" = defined by a framework, such as the `wp_loaded` hook in WordPress: https://github.com/wplib/wplib/blob/master/modules/posts/posts.= php#L68C28-L68C37 

6. Yes another use-case would be to make a = framework more robust and to provide guidance to a user for what they = are doing wrong. Here I am actually throwing an error when the it is = clear the classes are being initialized out-of-order: https://github.com/wplib/wplib/blob/master/wplib.php#L157
7. Still another is loading related .PHP files = that are required so as not to have to rely on the autoloader.
https://github.com/wplib/wplib/blob/master/wplib.php#L167.&= nbsp;

Here I would also suggest guidance about not = including any code that can fail at load time during the normal course = of execution meaning syntax errors are okay but the loaded code should = really never generate a runtime errors. 

8. A big use-case too is = allowing static classes to register themselves as being able to provide = a service to another class defined by the application or framework. This = allows a class to self-describe its behavior and capabilities such as = how my library allows the registration of the class as a "Helper" for = the main class and how it allows registering a class as representing a = "Role" in the framework: https://github.com/wplib/wplib/blob/master/modules/posts/posts.= php#L48 https://github.com/wplib/wplib/blob/master/modules/role-adminis= trator/role-administrator.php#L37 

This is much like how a = pub-sub system allows others to subscribe to the publisher and the = publisher does not need to know in advance all the subscribers to the = system. 

9. Regarding Larry's concern for test mocking, = if the only things that static initialize functions are doing is = initializing immutable data, registering hooks, pre-loading non-error = generating .PHP files, and/or registering classes for special use-cases = then there is #fyi (little or?) no need to mock them = for testing purposes. 

For developers following said guidance I do not = see static initializers as creating a challenge for testing.

-Mike

P.S. I think = it would be great if static initializers threw a runtime error if they = attempted to do anything that could throw an error =E2=80=94 at least = not without a try-catch to absorb the error =E2=80=94 but as I cannot = envision how that would be possible and still be acceptably performant I = think the only approach should be provide the community with guidance on = how and how not to use static initializers. #fwiw


= --Apple-Mail=_4C0093B9-4F20-48E5-B407-24A352A558C1--