# Initializing constants once, with code?

- Thread: https://externals.io/message/108238
- Original archive: https://news-web.php.net/php.internals/108238
- Started by tyson andre on 2020-01-25T03:37:22+00:00
- Messages: 40
- Page 1 of 2: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/108238.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #107961 — Mike Schinkel — 2020-01-02T17:35:11+00:00

URL: https://externals.io/message/108238#107961

Happy New Year Internals!

One of the areas I have been agonizing over in 2019 has been constants — especially class constants — and Mark Randall's following comment from the other thread cause me to remember that I had been planned to ask the list to discuss for a while.

> Begin forwarded message:
> From: Mark Randall <marandall@php.net>
> Subject: [PHP-DEV] Re: [RFC] "use global functions/consts" statement
> Date: January 1, 2020 at 11:31:48 PM EST
> To: internals@lists.php.net
> 
> That doesn't cover defined constants, but IMO we should be pushing those to be moved into class constants, simply because in the absense of function / constant / anything-but-class level autoloading, things might as well be in a readily available location.

I have found namespaced class constants as a great usable syntax for DRYing out code during refactoring related to typically hardcoded literal values, or when writing code initially. Once such example might be:
namespace Example;
class API {
   const BASE_URL = 'http://api.example.com';
}
With this a developer can use like so:
namespace Example;
....
$api_url = sprintf( '%s/%s', API::BASE_URL, '/widgets' );
Instead of writing this 20 different places across a codebase:
$api_url = 'http://api.example.com/widgets';

The problem with using constants is that the value is still hardcoded and if we later want to change to pulling the data from a config file we have to change all the code that uses those constants.  

Yes we could use methods instead, but frankly constants feel more natural for things that typically get hardcoded.

We all know that constants need to be, by definition, constant (a.k.a. immutable), but is there any reason we could not allow constants a one-time initialization via code?  That way we would allow a developer to hardcode a constant and then later come back and convert that constant to be initialized from a configuration file without having to modify any code that uses the constant?

If others agree with this concern then three (3) approaches come to mind, either of which would satisfactory to me although the first would be best IMO.  That said, I am open to any approach that allows constants to be one-time initialized with code.

Approach #1: Extend the const keyword to allow function-like syntax

Extend the constant keyword to support all the syntax supported by methods except parameters, but only run the code the first time the constant is accessed, on demand:

namespace Example;
class API {
   const BASE_URL:string {
      return Config::get_options('api_base_url');
   }
}
$api_url = sprintf( '%s/%s', API::BASE_URL, '/widgets' );
Approach #2: Allow one-time assignment to a constant

Alternately we could allow one-time assignment to a constant, although this would be much less preferable because it would require the initialization to be explicitly called by a developer, and the developer would have to implement all their own on demand logic, or just initialize all classes on page load which most people would probably fall back to doing:
namespace Example;
class API {
   const BASE_URL;
   static function initialize() {
      self::BASE_URL = Config::get_options('api_base_url');
    }
}
API::initialize();
$api_url = sprintf( '%s/%s', API::BASE_URL, '/widgets' );
Approach #3:Magic methods!

A magic method could work too, but I know many others have reasons to dislike them no the least of which is lack of static typing. I include here more for completeness rather than as a serious suggestion:
namespace Example;
class API {
   static function __const(string $const_name) {
      $value = null;
      if ( 'BASE_URL' === $const_name ) {
         $value = Config::get_options('api_base_url');
      } else {
         trigger_error( sprintf("constant %s::%s has not been defined.",
            `get_called_class()`,
            $const_name
         ));
      }
      return $value;
   }
}
$api_url = sprintf( '%s/%s', API::BASE_URL, '/widgets' );
Thank you for considering and I look forward to your thoughts.

-Mike

---

## #107963 — Bruce Weirdan — 2020-01-02T23:14:28+00:00

URL: https://externals.io/message/108238#107963 · In reply to: #107961

There's also a fourth approach that does not involve any changes to
PHP: autoload + eval.
When PHP is looking for your class, in your autoload you load the
values from config, generate the class source and eval() it.

---

## #107964 — Mike Schinkel — 2020-01-02T23:40:46+00:00

URL: https://externals.io/message/108238#107964 · In reply to: #107963

> 
> 
> There's also a fourth approach that does not involve any changes to
> PHP: autoload + eval.
> When PHP is looking for your class, in your autoload you load the
> values from config, generate the class source and eval() it.

Yes, I guess that would technically be possible.  

But I think in this case — dynamically generated code, lack of type safety and inability to version control and statically analyze the code — the proposed solution would be worse than the fallback position of not using constants.

-Mike

---

## #107965 — tyson andre — 2020-01-03T00:09:31+00:00

URL: https://externals.io/message/108238#107965 · In reply to: #107961

> The problem with using constants is that the value is still hardcoded
> and if we later want to change to pulling the data from a config file 
> we have to change all the code that uses those constants. 

It's possible to work around this by using a unique constant name
and define('unique_name', $dynamicExpression)
(and const MY_CLASS_CONST = \unique_name;)

Maybe php could add something along these lines:

local const X = sprintf('In %s', $dynamicValue);
// Not supported in functions or loops.
// Translates internally to:
// if (defined('some_globally_unique_constant123af') || 
//     !define('some_globally_unique_constant123af', X)) { throw ...; }
// use some_globally_unique_constant123af as X;
class MyClass {
    const PUBLIC = X;
}

- php still needs to prevent recursion such as 
  `const A = count(B); const B = array_fill(1, A, 'value');`,
  which is why the simplest approach would be one that eagerly evaluates dynamic expressions.
  That's probably not the only way - maybe it could track all of the constants that
  are in the process of being defined and cause an uncatchable fatal error if there was recursion.
- Right now, reading the value of a class constant can already emit notices or throw an Error (undeclared constants)
  the first time it gets evaluated
- Any approach should throw for results that contain cycles, objects, or references,
  the same way `define()` warns.

The below ugly proof of concept shows that the php runtime can in a sense
defer evaluation of dynamic class constants to runtime (not recursively)
(assume errstr/errfile control what constant gets loaded).
This POC should not be used anywhere.

<?php
set_error_handler(function ($errno, $errstr) {
    echo "Ignoring $errno: $errstr\n";
    define(
        'SOME_GLOBALLY_UNIQUE_CONSTANT',
        sprintf('Hello, %s', $GLOBALS['globalVar'])
    );
});
class MyTest {
    const KEY = 'myKey';
    const FOO = [
        self::KEY => [[][0], SOME_GLOBALLY_UNIQUE_CONSTANT][1],
    ];
}
$globalVar = 'world';
echo "Fetching MyTest::FOO\n";
var_dump(MyTest::FOO);
/*
Output:
Fetching MyTest::FOO
Ignoring 8: Undefined offset: 0
array(1) {
  ["myKey"]=>
  string(12) "Hello, world"
}
 */

---

## #107966 — Mike Schinkel — 2020-01-03T00:39:57+00:00

URL: https://externals.io/message/108238#107966 · In reply to: #107965

> 
> 
> > […]
> 
> It's possible to work around this by using a unique constant name
> and define('unique_name', $dynamicExpression)
> (and const MY_CLASS_CONST = \unique_name;)

I do appreciate the attempt to come up with something that is a workaround.

That said, I definitely do not want to do is start using "clever" code in my implementations because — one day — someone else will have to maintain what I write.

Really, I was just asking for a discussion of potentially allowing for one-time initialization via code for constants and via a straightforward, obvious syntax and standardized syntax.  

Effectively I am just asking for immutable static values in a class that can be access vis the <Class>::<ImmutableValue> syntax.

-Mike

---

## #107979 — Larry Garfield — 2020-01-03T16:53:09+00:00

URL: https://externals.io/message/108238#107979 · In reply to: #107966

> > […]
> 
> I do appreciate the attempt to come up with something that is a workaround.
> 
> That said, I definitely do not want to do is start using "clever" code 
> in my implementations because — one day — someone else will have to 
> maintain what I write.
> 
> Really, I was just asking for a discussion of potentially allowing for 
> one-time initialization via code for constants and via a 
> straightforward, obvious syntax and standardized syntax.  
> 
> Effectively I am just asking for immutable static values in a class 
> that can be access vis the <Class>::<ImmutableValue> syntax.
> 
> -Mike

I agree with the use case, but not with the solution of piggybacking them onto constants.  It seems to me it would be much cleaner to just... build that into a static method call with the derivation-and-caching logic behind the scenes.  It's not complicated code.

class Stuff
{
  protected $val;

  public static function val() {
    self::$val ??= computeStuff();
    return self::$val;
  }
}

Although property accessors might make it even nicer looking, if we can ever figure out how to make that performant. :-)

--Larry Garfield

---

## #107980 — Mike Schinkel — 2020-01-03T18:55:26+00:00

URL: https://externals.io/message/108238#107980 · In reply to: #107979

Hi Larry,

Thanks for replying.

>  It seems to me it would be much cleaner to just... build that into a static method call with the derivation-and-caching logic behind the scenes.  It's not complicated code.

That is exactly what I have been doing, and what I am finding suboptimal.

The problem is that, most of the time and especially when refactoring poorly written code, we just want a constant.  One line in the class file, hard coded.  

We ideally don't want to have to write a full functionr.  So for five data elements, 5 easy to read lines of code with constants vs. 20 harder to read lines with functions, or at least 40 lines if we include PHPDoc for those functions, which is part of our standard for writing functions.

When we *later* want to be able to refactor to initialize with code we want to do so w/o having to modify the code that is already using the constants. But we many never actually need to do so. What I am asking for is the option.

Basically, in my view, a programming language is better if it can support refactoring a reusable unit of code to add additional functionality W/O requiring the code that uses the reusable element to be modified.  Using your proposed approach we cannot start using constants and later move to functions unless we modify all code that uses those constants. That is Bad JuJu(tm) in my book.

OTOH, if PHP 8.x would allow functions w/o parameters to be called w/o parenthesis then your approach satisfy the use-case:

class Stuff {
    protected $val;

    public static function VAL() {
         self::$val ??= computeStuff();
         return self::$val;
    }
}
echo Stuff::VAL;   //<== if this could work then using functions would address the use-case.

> I agree with the use case, but not with the solution of piggybacking them onto constants. 

Let me put it more concretely.  What I am asking for is this:

> "Transitionally of reusable code when using constants that does not require modifying code that already uses the constant."

So using functions does not actually address the use-case I am identifying.

Is there some technical reason why you object to extending constants, or is it your objection more stylistic?

-Mike

---

## #107981 — Vesko Kenashkov — 2020-01-03T19:43:22+00:00

URL: https://externals.io/message/108238#107981 · In reply to: #107980

> ...
>
> OTOH, if PHP 8.x would allow functions w/o parameters to be called w/o
> parenthesis then your approach satisfy the use-case:
>
> class Stuff {
>     protected $val;
>
>     public static function VAL() {
>          self::$val ??= computeStuff();
>          return self::$val;
>     }
> }
> echo Stuff::VAL;   //<== if this could work then using functions would
> address the use-case.
>
> Hi everyone,

I just wanted to comment on the above - in my view we can not have
someclass::VAL be a constant or a function call of a function that has no
arguments. It is not that it just feels confusing but also may create
issues as a person may invoke multiple times someclass::VAL thinking that
this is a constant (cheap) while in fact it may be a method doing expensive
lookup. And my personal feeling is very much against mixing two completely
different things in this way. This reminds me of Matlab where A(4,5) may be
a function with two arguments, or a matrix lookup (things may have changed
since and may no longer be the case...).

And on a side note - the described problem I solve it in two ways:
- custom autoloader that invokes a initialization method that sets static
vars
- more recently - a class generator (as mentioned above). This gives no
performance hit as Im using Swoole and it happens only at server start.

Thanks
Vesko

-- 
Vesko Kenashkov
Developer at AzonMedia <https://azonmedia.com/> and Guzaba Framework
<https://github.com/AzonMedia/guzaba2

---

## #107982 — Larry Garfield — 2020-01-03T19:52:43+00:00

URL: https://externals.io/message/108238#107982 · In reply to: #107980

> Hi Larry,
> 
> Thanks for replying.
> 
> > […]
> 
> That is exactly what I have been doing, and what I am finding suboptimal.
> 
> The problem is that, most of the time and especially when refactoring 
> poorly written code, we just want a constant.  One line in the class 
> file, hard coded.  
> 
> We ideally don't want to have to write a full functionr.  So for five 
> data elements, 5 easy to read lines of code with constants vs. 20 
> harder to read lines with functions, or at least 40 lines if we include 
> PHPDoc for those functions, which is part of our standard for writing 
> functions.
> 
> When we *later* want to be able to refactor to initialize with code we 
> want to do so w/o having to modify the code that is already using the 
> constants. But we many never actually need to do so. What I am asking 
> for is the option.
> 
> Basically, in my view, a programming language is better if it can 
> support refactoring a reusable unit of code to add additional 
> functionality W/O requiring the code that uses the reusable element to 
> be modified.  Using your proposed approach we cannot start using 
> constants and later move to functions unless we modify all code that 
> uses those constants. That is Bad JuJu(tm) in my book.
> 
> OTOH, if PHP 8.x would allow functions w/o parameters to be called w/o 
> parenthesis then your approach satisfy the use-case:
> 
> class Stuff {
>     protected $val;
> 
>     public static function VAL() {
>          self::$val ??= computeStuff();
>          return self::$val;
>     }
> }
> echo Stuff::VAL;   //<== if this could work then using functions would 
> address the use-case.
> 
> > […]
> 
> Let me put it more concretely.  What I am asking for is this:
> 
> > […]
> 
> So using functions does not actually address the use-case I am identifying.
> 
> Is there some technical reason why you object to extending constants, 
> or is it your objection more stylistic?
> 
> -Mike

There's two broad reasons I think this is a bad idea.

1) Constants are one thing.  Function calls are another.  They serve different purposes.  Trying to mutate them into one thing can only lead to confusion and lack of understanding about what is actually going on.

2) The approach you describe (of starting with constants everywhere and refactoring to method calls later)... I would never do and do not endorse.  What you describe is basically "make globals nicer to work with", whereas I am 100% firmly in the camp of "if I could remove globals from the language entirely I would".  Frankly, the use of constants for configuration is an anti-pattern to begin with; they should be used only for things that are truly constant.  Honestly, I cannot recall the last time I used constants for anything other than giving some other compile-time value a nicer name.  (Eg, DEFAULT_THING_VALUE or giving nice names to bit flags or something like that.)

For configuration, my answer is frankly "put your configuration behind a nice configuration object from the very beginning and then you won't have to refactor it later; Problem solved."  You can use env vars for configuration, and wrap those into a nice object, possibly using one of the many DotEnv implementations that already exist to make them nicer to work with in development.  That is superior in basically every conceivable way to semi-mutable globals passing themselves off as pseudo-constants.

I *would* love to see property accessors come back, which would have a side effect of making what you describe a little easier, but at no point is it pretending to be a compile time value when it isn't.

--Larry Garfield

---

## #107986 — Mike Schinkel — 2020-01-04T21:25:33+00:00

URL: https://externals.io/message/108238#107986

> 
> 
> There's two broad reasons I think this is a bad idea.
> 
> 1) Constants are one thing.  Function calls are another.  They serve different purposes.  Trying to mutate them into one thing can only lead to confusion and lack of understanding about what is actually going on.
> 
> 2) The approach you describe (of starting with constants everywhere and refactoring to method calls later)... I would never do and do not endorse.  What you describe is basically "make globals nicer to work with", whereas I am 100% firmly in the camp of "if I could remove globals from the language entirely I would".  Frankly, the use of constants for configuration is an anti-pattern to begin with; they should be used only for things that are truly constant.  Honestly, I cannot recall the last time I used constants for anything other than giving some other compile-time value a nicer name.  (Eg, DEFAULT_THING_VALUE or giving nice names to bit flags or something like that.)
> 
> For configuration, my answer is frankly "put your configuration behind a nice configuration object from the very beginning and then you won't have to refactor it later; Problem solved."  You can use env vars for configuration, and wrap those into a nice object, possibly using one of the many DotEnv implementations that already exist to make them nicer to work with in development.  That is superior in basically every conceivable way to semi-mutable globals passing themselves off as pseudo-constants.
> 
> I *would* love to see property accessors come back, which would have a side effect of making what you describe a little easier, but at no point is it pretending to be a compile time value when it isn't.
> 
> --Larry Garfield

<sigh>  So much to unpack here, but I will just hit the most important points.

You can say "Problem solved" but only because you redefined the problem into an idealistically convenient use-case:

	1. IF you have full control of all the code, 
	2. IF you are starting from scratch and do not have legacy code to contend with, 
	3. And IF you and your team are not limited by budget, timeline or developer skill.

In the real world however you don't often get those ideal scenarios.

It is irrelevant if you would never use constants or not endorse the approach because many developers have used constants in the past and continue to do so. I am proposing a solution to address a problem and you are proposing idealogical purity.To give an analogy, it would be like me asking Congress to address teen pregnancy and you being a congressman who first spoke and said "Let's tell them not to have sex. Problem solved."

But we can argue *opinions* all day long. Let me provide some real-world examples and some numbers.  

Symfony — which many PHP developers hold in high regard including Drupal developers — uses constants:

	https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
	https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Intl/Locale/Locale.php
	https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php
	https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Workflow/TransitionBlocker.php
	https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Intl/Intl.php
	https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Security.php
	https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

On the other end of the spectrum are WordPress plugins. I maintain a *somewhat* complete mirrors of the plugins database from WordPress.org which I download from here http://plugins.svn.wordpress.org/. 

I generated a list of `const <symbol> =` found across all plugins using this command:

grep "const\s+[a-zA-Z1-9_]+\s*=" -RE --include \*.php . > const.txt

This produced a file containing 455,068 constants.  That's a lot of constant usage for something you argue should never be done.

Using this command I calculated that there were 338,520 constants that were NOT integer or float.  

	cat const.txt | grep -E "=\s*-?\d+(\.\d+)?\s*;"  | wc -l

A lot of them (~20%?) were to create array keys that could be referenced in a type-safe manner, e.g. from Guzzle:

	From 1-click-migration/vendor/guzzlehttp/guzzle/src/RequestOptions.php

		const ALLOW_REDIRECTS = 'allow_redirects';
		const AUTH = 'auth';
		const BODY = 'body';
		const CERT = 'cert';
		const COOKIES = 'cookies';
		const CONNECT_TIMEOUT = 'connect_timeout';
		const DEBUG = 'debug';
		const DECODE_CONTENT = 'decode_content';
		const DELAY = 'delay';
		const EXPECT = 'expect';
		const FORM_PARAMS = 'form_params';
		const HEADERS = 'headers';
		const HTTP_ERRORS = 'http_errors';
		const JSON = 'json';
		const MULTIPART = 'multipart';
		const ON_HEADERS = 'on_headers';
		const ON_STATS = 'on_stats';
		const PROGRESS = 'progress';
		const PROXY = 'proxy';
		const QUERY = 'query';
		const SINK = 'sink';
		const SYNCHRONOUS = 'synchronous';
		const SSL_KEY = 'ssl_key';
		const STREAM = 'stream';
		const VERIFY = 'verify';
		const TIMEOUT = 'timeout';
		const READ_TIMEOUT = 'read_timeout';
		const VERSION = 'version';
		const FORCE_IP_RESOLVE = 'force_ip_resolve';

Using this command I found that 17,908 constants were used to hardcode URLs, one of the more frequent use-cases I am trying to address:

	cat const.txt | grep -E "https?://" | wc -l

For just a few examples:

	From 1stpaygateway-for-woocommerce/includes/class-wc-gateway-1stpay.php  

		const URL_API = "https://secure.1stpaygateway.net/secure/RestGW/Gateway/Transaction/";
		const URL_API_VALIDATION = 'https://secure-v.goemerchant.com/secure/RestGW/Gateway/Transaction/';
		const URL_TRANS_CENTER_SUPPORT = 'http://support.goemerchant.com/transaction-center.aspx';
		const URL_GATEWAY_OPTIONS_SUPPORT = "http://support.goemerchant.com/transaction-center.aspx?article=gateway-options";
		const URL_SUBMIT_CC_BATCH_SUPPORT = "http://support.goemerchant.com/transaction-center.aspx?article=submit-credit-card-batch";

	From ad-kangaroo-for-adsense/google_api/Auth/OAuth2.php:  

		const OAUTH2_REVOKE_URI = 'https://accounts.google.com/o/oauth2/revoke';
		const OAUTH2_TOKEN_URI = 'https://accounts.google.com/o/oauth2/token';
		const OAUTH2_AUTH_URL = 'https://accounts.google.com/o/oauth2/auth';

I scanned through the const.txt file with `less` for the non-numeric constants using this command (of which there were 338,520):

	cat const.txt |grep -E -v "=\s*-?\d+(\.\d+)?\s*;" | less

I anecdotally found constants used for the following use-case, many of which are not "truly" constants and thus could benefit from being able to be initialized in the future from a configuration source w/o breaking any client code that used said constants:

	1.  SVG code for icons
	2.  Regular expressions (3989 uses where the constant contains 'REGEX' in the name)
	3.  HTML snippets
	4.  CSS colors
	5.  XML Schema URLs and/or header elements 
	6.  Error messages
	7.  User prompts
	8.  Date/Time formats
	9.  File extensions
	10. Version numbers
	11. WordPress Option names
	12. Output formats (e.g. 'json', 'xml', 'csv', etc.)
	13. Mustache/Handlebar templates
	14. HTTP Extension Headers (e.g. "X-*")
	15. Browser user-agent signatures
	16. Payment processor codes
	17. API/Server Error Codes
	18. Bitmaps
	19. HTTP verbs
	20. GUIDs
	21. HTML element mapping
	22. Currency codes
	23. PHP for eval()/code generation
	24. UPS/Fedex codes
	25. Relative API endpoints

Those are just from "1"-"9" and "a"; there are probably another 10 or 20 categories in "b" thru "z."

Then using this command I calculated that there were 116,548 constants that were integer or float.  

	cat const.txt | grep -E "=\s*-?\d+(\.\d+)?\s*;"  | wc -l

Most of those numeric constants would likely fit in your "truly" constant category, but anecdotally a significant number would not; I estimate ~5%, or ~5777. 

For those that are not "true" constants, a developer might want to allow an end user to configure them. But because they chose to hardcode as constants initially the plugin can never evolve their code to allow for configuration without making *breaking* changes.  

For just a *few* examples:

	const RATIO = 0.5625;  
	const DEFAULT_FILE_UPLOAD_REQUEST_TIMEOUT = 3600;
	const MAX_LINE_LENGTH = 998;

	const AUTH_TOKEN_LIFETIME_SECS = 300;
	const CACHE_LIFETIME = 86400;
	const THUMB_WIDTH = 300;
	
	const NUMBER_COLS_REPEATED_MAX = 1024;
	const COMPUTE_PING_CONNECTION_TIMEOUT_S = 0.5;  
	const DEFAULT_MEMSIZE = 10000;
	
	const BATCH_WRITE_MAX_SIZE = 25;   
	const FILES_PER_CHUNK=600;
	const WRITE_INTERVAL = 10000;
	
	const TIMEOUT_PRECISION = 0.2;
	const MIN_DURATION = 0.1;
	const MAX_DURATION = 15.0;

	const DEFAULT_CONCURRENCY = 5;

Those constants came from these plugins, respectively:

	accelerated-mobile-pages/includes/vendor/amp/includes/embeds/class-amp-youtube-embed.php
	accesspress-facebook-auto-post/api/facebook-mobile/Facebook/FacebookClient.php
	acymailing/front/inc/phpmailer/phpmailer.php
	
	ad-inserter/includes/google-api/Auth/OAuth2.php
	add-amazon-block/lib/amazonjs/amazonjs.php
	addon-library/inc_php/unitecreator_globals.class.php
	
	advanced-cf7-db/admin/class/PHPExcel/Writer/OpenDocument/Content.php
	amazon-s3-and-cloudfront/vendor/Gcp/google/auth/src/Credentials/GCECredentials.php
	amazon-s3-and-cloudfront/vendor/Gcp/google/auth/src/Cache/SysVCacheItemPool.php
	
	amazon-s3-uploads/lib/aws-sdk-php/Aws/DynamoDb/Model/BatchRequest/WriteRequestBatchTransfer.php
	anybackup/includes/BitsBackupWorker.php
	appilder-woocommerce-mobile-app-manager/inc/push-notification/ApnsPHP/Abstract.php
	
	assetsminify/vendor/symfony/process/Symfony/Component/Process/Process.php
	auto-post-to-instagram/vendor/mgp25/instagram-php/src/Media/Constraints/DirectConstraints.php
	auto-post-to-instagram/vendor/mgp25/instagram-php/src/Media/Constraints/DirectConstraints.php

	aws-auto-ses/vendor/aws/aws-sdk-php/src/Multipart/AbstractUploader.php

Note the above examples I give all started with "a." I did not give any examples from "1" thru "9" and "b" thru "z" etc.:

Anecdotally there was a dompdf package used by many plugins that makes heavy use of constants for non "truly" constant values, e.g:

	From https://github.com/dompdf/dompdf/blob/master/src/FrameDecorator/ListBullet.php
	const BULLET_PADDING = 1; // Distance from bullet to text in pt
	const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04
	const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now.
	const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent.

I think this analysis proves many PHP developers do use constants in ways you claim you would never.  IOW, the use-cases I am trying to address exist to a significant degree in the wild.

Further I believe I have made the case that there would be significant value in allowing developers who previously used constants because of prior lack of skill or too-tight timelines to be able to evolve their code to initialize their  constants with code instead of forcing their users to change their code or worse.

-Mike

---

## #107987 — Mike Schinkel — 2020-01-04T22:40:58+00:00

URL: https://externals.io/message/108238#107987

> 
> 
> This is, as so often with such comparisons, an exaggeration: the docblock for a function need not be any longer than the docblock for a constant, and the function body can be simplified.

Fair point.  

> Add in the "initialise on first read", and you end up with 4 lines per pseudo-constant vs 5 per method:

But then you give an example which exaggerates in the other direction.  

The point was when constants ARE used they result in a lot more compact and infinitely more scannable code. Thus when you do not actually need to use initialization code you do not have to and you get much more scannable code, but you have the _option_ to add initialization code _later_:

class Environments {
   const PRODUCTION = 'master';
   const STAGING = 'stage';
   const TESTING = 'test';
   const DEVELOPMENT = 'dev';
}

vs. 

class Environments {
   static function PRODUCTION() {
      return 'master';
   }
   static function STAGING() {
      return 'stage';
   }
   static function TESTING() {
      return 'test';
   }
   static function DEVELOPMENT() {
      return 'dev';
   }
}

What I am asking for is the ability to _evolve_ code over time and not force code to forever retain its technical debt simply because of initial choices. 

And not just for constants, but for evolvability of all code. To me that seems like such a no-brainer best practice for programming language design that I am struggling to understand the fundamental objections to it.

> Overall, I agree with Larry that these aren't really constants, and shouldn't be treated as such.

The fact constants are limited to literal code is merely a limitation of the opcode compiler, not a conceptual difference.  In both cases they are initialized before use, and then never changed. If they are immutable once initialized, then they are conceptually constants. 

And if the opcode compiler could offer more expansive initialization than just literals then my use-cases would likely be addressed.

Another approach would be for me to ask for an `immutable` option that has the behavior I am proposing, but that would not empower the millions of constants in the wild that already exist to evolve and would only benefit new code:

class Environments {
   immutable PRODUCTION = 'master';
   immutable STAGING = 'stage';
   immutable TESTING = 'test';
   immutable DEVELOPMENT = 'dev';
}

Yet another approach would be for me to ask for would be for a shortened function declaration syntax. But again that would also not empower existing constants to evolve:

class Environments {
   function PRODUCTION = 'master';
   function STAGING = 'stage';
   function TESTING = 'test';
   function DEVELOPMENT = 'dev';
}

> Property accessors, or some native support for lazy-loading properties (which would stop Doctrine needing to do tricks with unset() and __get), would seem like a better fit for the use case.

I would also LOVE property accessors added to PHP, but I disagree it would address the use-case of existing constants in libraries in the wild (and in future code) that are forever cursed to remain as hardcoded literals to ensure backward compatibility.

Both you and Larry have redefined my use-case to an idealogically convenient use-case.  Can we please address the use-case as I am defining it: existing code that already uses constants and future code where developers use constants because of lack of skill, awareness of "best practices", or too-tight timelines?  

Maybe there are other solutions for the use-case I am trying to address besides the one I proposed, but we can't get there if everyone redefines the use-case to support their arguments.

-Mike

---

## #107989 — Larry Garfield — 2020-01-05T17:02:22+00:00

URL: https://externals.io/message/108238#107989 · In reply to: #107986

> > […]
> 
> <sigh>  So much to unpack here, but I will just hit the most important points.

*snip*

> Symfony — which many PHP developers hold in high regard including 
> Drupal developers — uses constants:

Just an FYI: I assume you mention Drupal specifically here because of my history with the project.  What Drupal developers hold in high regard matters not even 0% for me at this point; "Drupal does it" is not an argument that will carry any weight with me.

*snip*

> I think this analysis proves many PHP developers do use constants in 
> ways you claim you would never.  IOW, the use-cases I am trying to 
> address exist to a significant degree in the wild.
> 
> Further I believe I have made the case that there would be significant 
> value in allowing developers who previously used constants because of 
> prior lack of skill or too-tight timelines to be able to evolve their 
> code to initialize their  constants with code instead of forcing their 
> users to change their code or worse.
> 
> -Mike

I think you are overstating my case.  I am not arguing that one should never use constants.  In fact, a majority of the examples you cite I would say are reasonable uses of constants, at least in concept.  (I cannot speak to the particulars of the individual code bases.)

I am saying that the behavior of a constant is that it is... constant.  A proper constant is evaluated at compile time and appears as a literal in the final runtime code, with no dereferencing.  AIUI, `const` in PHP does exactly that, in contrast to `define`.  That is fundamentally conceptually incompatible with a value that is derived at runtime and happens to use constant-style syntax.  I am firmly opposed in principle to having a language construct that looks like a constant and makes you think it's a constant but is actually a method call that does who knows what.

There's really two different use cases here, I think, that we're conflating.  ("We" meaning I think we're all doing it to an extent.)

1) I want an approximately global value that is auto-memoized on first usage, so I can access it from anywhere without having to recompute it or do the work of memoizing it myself.

2) I have a code base with lots of constants, and I've changed my mind about the API (for whatever reason) and want them to no longer be constant but runtime defined.

For point 1, I'd love it if we had some way to auto-memoize a result.  That would be sweet.  There's a couple of ways we could go about it... but I firmly believe that sticking it behind a const-like API is the wrong way to do it for the reasons above.  (I wouldn't want to limit it to global-ish things, either, because global-ish things are, as stated, bad in every way and should be avoided whenever possible.  Yes, I know there's a lot of bad code out there already with it; that doesn't mean encouraging it.)

For point 2... if you want to change your API, then your API is going to change.  That's what it means to change an API.  It's no different than deciding to rename a method or class.  It may or may not be hard depending on the API and the install base, but that it's a constant doesn't change matters.  Making code easier to refactor is all well and good, but not if it violates all expectations around what a constant is.

For derive-once global constants, you may be looking for `define`.  That's a runtime operation and effectively boils down to a set-once global.  It doesn't help with constants defined in a class, but for an already-global constant `define` already gives you a one-time set operation, just not on-demand.  That's probably the better avenue to pursue for option 2, though again it's not one that I find worth compromising the language design for.

--Larry Garfield

---

## #107992 — Rowan Tommins — 2020-01-05T18:24:39+00:00

URL: https://externals.io/message/108238#107992 · In reply to: #107987

> > […]
> But then you give an example which exaggerates in the other direction.


No, I gave an example of exactly what it would like if I wrote it: one 
line methods with one line docblocks, or one line constants with one 
line docblocks. The only difference in your example below is that you've 
omitted the docblocks from both versions, saving one line on each.


> class Environments {
>     const PRODUCTION = 'master';
>     const STAGING = 'stage';
>     const TESTING = 'test';
>     const DEVELOPMENT = 'dev';
> }
>
> vs.
>
> class Environments {
>     static function PRODUCTION() {
>        return 'master';
>     }
>     static function STAGING() {
>        return 'stage';
>     }
>     static function TESTING() {
>        return 'test';
>     }
>     static function DEVELOPMENT() {
>        return 'dev';
>     }
> }


This is a really terrible example, because it's basically an enum, and 
would never be anything other than a set of true constants. In a sense, 
that's nitpicking at the example, but I think it's teling: the kinds of 
constants that are "scannable" are generally not the kind that would be 
replaced by configuration in the way you're suggesting.

That is, if you have:

class API {
     const TEST_URL = 'https://testing.api.example.com';
     const LIVE_URL = 'https://production.api.example.com';
}

...then these are nicely "scannable", but already capture the fact that 
there are different options to choose from.

If instead you have:

class API {
     const URL = 'https://production.api.example.com';
     const VERSION = 1.5;
}

...then being "scannable" isn't particularly relevant, because these are 
completely separate values.


> What I am asking for is the ability to _evolve_ code over time and not force code to forever retain its technical debt simply because of initial choices.
>
> And not just for constants, but for evolvability of all code. To me that seems like such a no-brainer best practice for programming language design that I am struggling to understand the fundamental objections to it.


I suppose it would be possible to design a language which maximised 
"evolvability" by using interchangeable syntax for different concepts. 
For instance, properties and constants could all just be special method 
calls; perhaps "const FOO=42" would be sugar for "static function FOO() 
{ return 42; }" and "SomeClass::FOO" would always be the same as 
"SomeClass::FOO()".

However, there will always be new requirements that require a breaking 
change. For instance, maybe you need to send some API calls to one URL 
and some to another, so everywhere that fetches the URL needs to choose 
between two constants, or pass a parameter to the method. Or maybe you 
write something as static and later wish you'd required an instance to 
be constructed.

So it's not enough to say "everything should be evolvable", we need to 
ask whether this specific scenario is common enough to design the 
language around, and what the possible downsides of the feature might be.


> The fact constants are limited to literal code is merely a limitation 
> of the opcode compiler, not a conceptual difference. In both cases 
> they are initialized before use, and then never changed. If they are 
> immutable once initialized, then they are conceptually constants. 


I disagree, I think constants being a literal value is a design 
decision, not an implementation one. Off the top of my head, I can think 
of three types of things that you might call "constants":

1. A value that will never change, and you just want to give a name to. 
For instance, MINUTES_IN_HOUR=60
2. A value set at compile-time, to hard-code a particular condition. For 
instance, DEBUG=false
3. A value which is set at run-time, but happens not to change during 
the course of the application, so should not be over-written.

PHP's constants are designed primarily to be type 1 - they are literal 
values, hard-coded in the source. Unlike C, there is no standard 
pre-processor for PHP, so type 2 is rarer, but it's possible to 
implement by manipulating the source file before deployment, or even 
when running the autoloader.

Your proposed feature changes constants to instead be type 3, which I 
think is a fundamentally different concept - much more like a property 
with extra restrictions. I think it would be very confusing for the 
syntax which everyone is used to meaning "hard-coded or at most 
compile-time value" could now actually mean "global value different on 
every request":

const USER_ID {
     return $_GET['user_id'];
}



> Yet another approach would be for me to ask for would be for a shortened function declaration syntax. But again that would also not empower existing constants to evolve:
>
> class Environments {
>     function PRODUCTION = 'master';
>     function STAGING = 'stage';
>     function TESTING = 'test';
>     function DEVELOPMENT = 'dev';
> }


During the discussion of short closures a suggestion came up that the 
syntax could be reused for single-expression methods, so you'd write this:

class Environments {
    public static fn getUrl() => 'https://api.example.com';
}

As you say, that still requires callers to know that they're calling a 
method, not referencing a constant; we evidently disagree on whether 
that's a good thing.


> Both you and Larry have redefined my use-case to an idealogically 
> convenient use-case. Can we please address the use-case as I am 
> defining it: existing code that already uses constants and future code 
> where developers use constants because of lack of skill, awareness of 
> "best practices", or too-tight timelines?


For my part, I tried to look at the bigger picture, because I think 
major changes to the language should address as many use cases as 
possible, not introduce special syntax for narrow scenarios.

As Larry mentions, the proposal is also limited by "globalness". Since 
constants are always static, the initialisation code can only access 
global and static state, so you can't inject a configuration object into 
your constructor then use it to initialise constants. And since they're 
write-once, you can't use them for Laravel-style "facades", where the 
access is static, but can be swapped out at run-time.


Perhaps a better angle to look at is something like the C Pre-Processor 
- manipulating the code at compile-time so that multiple different 
versions of the code can be created from the same source, but constants 
are still constant at run-time. I know there are tools out there already 
to do this, but have never used one.


Regards,

-- 
Rowan Tommins (né Collins)
[IMSoP]

---

## #107993 — Mike Schinkel — 2020-01-05T18:34:58+00:00

URL: https://externals.io/message/108238#107993 · In reply to: #107989

> 
> 
> I am saying that the behavior of a constant is that it is... constant.  A proper constant is evaluated at compile time and appears as a literal in the final runtime code, with no dereferencing.  AIUI, `const` in PHP does exactly that, in contrast to `define`.  That is fundamentally conceptually incompatible with a value that is derived at runtime and happens to use constant-style syntax.  

Here is the problem with the crux of your argument. You are stating as a postulate that which you have not proven. The only reason constants are defined at compile time is that they have always been defined at compile time. 

There is _no_ reason — unless you can elaborate a specific tangible reason — why a constant _must_ be defined at compile time vs. first access.  Doing the later does not change its essential state, i.e. it will still be "constant" and not change.

You are arguing from the perspective of a status-quo basis that constants are compile because they thus far have been compile-time. But preexisting implementation is not a valid argument for why something must continue to be the way it is.  Give me a conceptually valid reason why constants can only be defined a compile time and I may come to agree with you.  Otherwise I assert your arguments are just biased on current implementation.

> For derive-once global constants, you may be looking for `define`.  That's a runtime operation and effectively boils down to a set-once global.  It doesn't help with constants defined in a class, but for an already-global constant `define` already gives you a one-time set operation, just not on-demand.  


Let's consider the fact that is PHP had never had the `define()` and instead implemented the `const` keyword from the start, and only allowed compile time values. If PHP had done that, you would be arguing from a status quo bias the same thing about making that dynamic. And it currently is dynamic which IMO invalidates your argument that constants must be defined at compile time.

> I am firmly opposed in principle to having a language construct that looks like a constant and makes you think it's a constant but is actually a method call that does who knows what.

Can explore why you are opposed to is, and see if we can find a way to address the use-case I proposed in another way?

> 1) I want an approximately global value that is auto-memoized on first usage, so I can access it from anywhere without having to recompute it or do the work of memoizing it myself.

I would rewrite this to say "I want a convenient syntax for values that tend to get hardcoded by developers that is easy to use and easy to read that is initialized on first usage so that it behaves like a constant."

> (I wouldn't want to limit it to global-ish things, either, because global-ish things are, as stated, bad in every way and should be avoided whenever possible.  Yes, I know there's a lot of bad code out there already with it; that doesn't mean encouraging it.)

I am going to argue that glob-ish things are _not necessarily_ bad in every way. They are bad when they introduce coupling and when they are mutable. 

Global-ish is much less bad when immutable, and when structured to make tracking down usage easy.

But before you or anyone attacks me for saying this, realize that PHP is filled with globals that you happily use every day.  Class names are globals. Namespaces are globals. Functions are globals. And AFAICT immutable class constants are no worse globals than class names. 

You can easily find where class constants are defined, and with a PHP analysis tool you can easily find every place where they are used, except for when people are using reflection or dynamic dispatch but they can do the same with classes and namespaces, so that's not an argument against class constants.

> For point 2... if you want to change your API, then your API is going to change.  That's what it means to change an API. 

No, I said that I wanted the API to _not_ change. You are the one forcing the change constraint here. I am arguing code should be evolvable so that APIs do not need to change.

The more a language forces an API to change when requirements change, the more brittle and less fit-for-purpose the language is. The more a language does not allow refactoring without breaking an API the more it is a bad language, and that is much worse than some global constants IMO.

> Making code easier to refactor is all well and good, but not if it violates all expectations around what a constant is.

Let's explore the second part of this.

Languages change. And some times that changes expectations. To set the constraint that expectations cannot change is to force a language to be unable to improve.

Some examples of expectation change:

- PHP 7.4 preloading - My expectation is that the class files will be loaded on every page load, but with preloading they are not.  
- PHP 7.4 custom object serialization - My expectation is that the class instance will serialize as the class was defined, but this changes that.

- PHP 7.3 const case-insensitive deprecation - My expectation that is constants are case insensitive, but now my expectation is no longer valid.
- PHP 7.3 const string search functions with integer needle - My expectation that is I can pass an integer, but now my expectation is no longer valid.

I could go on, but I think the point is the change in expectation is not an unreasonable thing to occur when a language evolves. We all have to continue learning.

> That's probably the better avenue to pursue for option 2, though again it's not one that I find worth compromising the language design for.

It many be changing PHP in a way you object to. But it does not "compromise the language design." That phrasing is a bit inflammatory. 

-Mike

---

## #107995 — Mike Schinkel — 2020-01-05T19:08:42+00:00

URL: https://externals.io/message/108238#107995 · In reply to: #107992

> 
> 
> > […]

How are enums a terrible example? They are exactly my use-case. PHP does not have declared enums, so class constants suffice.

BTW, what are "true constants" in this context?  For one of my client's these get changes based upon the APIs environments that have the updated data.

> In a sense, that's nitpicking at the example, but I think it's teling: the kinds of constants that are "scannable" are generally not the kind that would be replaced by configuration in the way you're suggesting.
> 
> If instead you have:
> 
> class API {
>     const URL = 'https://production.api.example.com';
>     const VERSION = 1.5;
> }
> 
> ...then being "scannable" isn't particularly relevant, because these are completely separate values.

The benefits of being scannable are not limited to enum use-cases. The above is more scannable than the following, especially if I had 10+ values:

class API {
   function `URL()`: string{ 
      return 'https://production.api.example.com';
   }
   function VERSION(): string{ 
      return 1.5;
   }
}

So I don't get your arguments.

> I suppose it would be possible to design a language which maximised "evolvability" by using interchangeable syntax for different concepts. For instance, properties and constants could all just be special method calls; perhaps "const FOO=42" would be sugar for "static function FOO() { return 42; }" and "SomeClass::FOO" would always be the same as "SomeClass::FOO()".

NOW you are talking!  :-)

> However, there will always be new requirements that require a breaking change.

Clearly. But inability to optimize for perfect should not disallow optimizing for good.

> So it's not enough to say "everything should be evolvable", we need to ask whether this specific scenario is common enough to design the language around, and what the possible downsides of the feature might be.

Agreed.

> I disagree, I think constants being a literal value is a design decision, not an implementation one.

How is this not just status-quo bias?

> Off the top of my head, I can think of three types of things that you might call "constants":
> 1. A value that will never change, and you just want to give a name to. For instance, MINUTES_IN_HOUR=60
> 2. A value set at compile-time, to hard-code a particular condition. For instance, DEBUG=false
> 3. A value which is set at run-time, but happens not to change during the course of the application, so should not be over-written.

While I totally agree with #1 there are constants like hours that will always have 60 minutes and #3 is my use-case, I don't see many values for #2 that I would not want to be able to change at runtime.

> Your proposed feature changes constants to instead be type 3, which I think is a fundamentally different concept - much more like a property with extra restrictions.

Again, how is this not just status-quo bias?

> I think it would be very confusing for the syntax which everyone is used to meaning "hard-coded or at most compile-time value" could now actually mean "global value different on every request":
> 
> const USER_ID {
>     return $_GET['user_id'];
> }

As I just replied to Larry, there are continuous changes in PHP that require developers to learn new features. How is this just not a new feature to learn?

What if instead of the above we allow this?

immutable USER_ID {
    return $_GET['user_id'];
}

Then const would be for type #1 and immutable would be for type #2 and #3. But they would both be accessed by ClassName::USER_ID. This would meet my objective for evolvability of code. 

You can say that people can't know which one it is when they use it, but I would ask why can't we expect them to know the API they are using?

BTW, if such a thing were added to PHP then we would document it to so that the code contained within must be very robust and be limited to the types of things that cannot fail, but if they do fail then failure should be handled internally and a proper value should always be returned.

> During the discussion of short closures a suggestion came up that the syntax could be reused for single-expression methods, so you'd write this:
> 
> class Environments {
>    public static fn getUrl() => 'https://api.example.com';
> }
> 
> As you say, that still requires callers to know that they're calling a method, not referencing a constant; we evidently disagree on whether that's a good thing.

I am assuming in this case I would have to call it like Environments::getUrl().  So this would address the use-case for people who proactively choose to use functions instead of constants.  IOW, it would be a 1/2 win for my proposed use-cases.


> As Larry mentions, the proposal is also limited by "globalness". Since constants are always static, the initialisation code can only access global and static state, so you can't inject a configuration object into your constructor then use it to initialise constants.

I don't see why this is a limitation:

class API {
   function `URL()`: string{
      $config_class = defined('CONFIG_CLASS')
         ? CONFIG_CLASS
         : 'DefaultConfig';
      $config = new $config_class();
      return $config->get('api_url');
   }
}


> And since they're write-once, you can't use them for Laravel-style "facades", where the access is static, but can be swapped out at run-time.

Since I am not up to speed on Laravel, I am not sure what you mean here by "swapped out at runtime."  Laravel can have different facades are different times during the page load?  Wow, that seems worse that having an initialized constant immutable for the entire page.

That said, it would show that developers can and do handle changes in expectation.

> Perhaps a better angle to look at is something like the C Pre-Processor - manipulating the code at compile-time so that multiple different versions of the code can be created from the same source, but constants are still constant at run-time. I know there are tools out there already to do this, but have never used one.

Oh God no!  Pre-processors are definitely an anti-pattern, in my book.  I came to that conclusion sometime in the 80's and have yet to see anything that changed my mind.

-Mike

---

## #107996 — Mike Schinkel — 2020-01-05T19:19:24+00:00

URL: https://externals.io/message/108238#107996 · In reply to: #107992

Another use-case that just occurred to me is to support testing:

class Api {
   const URL: string{
      return $_ENV['EXAMPLE_API_URL'] ?? 'https://api.example.com';
   }
}

---

## #107998 — Mike Schinkel — 2020-01-05T21:53:24+00:00

URL: https://externals.io/message/108238#107998

> 
> 
> They seem a poor example of something that you would later want to "evolve" using the proposed new feature. An enum is, by definition, a set of values that covers all possibilities, often with completely arbitrary values.

Yet you called my list of URLs enums, and they are not at all arbitrary and absolutely want to change.

We can both agree that enums for 0, 1, 2 etc. are not in use-case I am discussing.

Maybe you misspoke when you called my list of URLs enums?  Or I misread?

> I'm struggling to imagine how this would work. I've seen plenty of code that needs to know "the current environment", but "the name of the current development environment" seems an odd thing to have a constant for.

I take it you have never used Pantheon hosting then?  You create a named environment to match a Git branch. 

"dev" is not "local" in this content.  "dev" is the place developers push code to test remotely.  Then they push to "test" for QA to evaluate.  At least for this client.

That said, my client wants to move to many "dev" and "test" environments, one for each JIRA epic, so we would like to be able to initialize the API urls based on the current subdomain. 

> Well, it would be more "scannable" if every method in the class was written on one line - and indeed you can do so if you want - but that's not inherently better.

What is "better" in this context is one's opinion.  You might not find it beneficial, but I would. 

> If a class has a bunch of different functions and constants in, I'll generally use an IDE or editor plugin to navigate them, and rely on them being well-named, rather than minimising the whitespace between them.

I use and have used PhpStorm extensively, since prior to 1.0.   But PhpStorm does not make it easier to do code reviews on Github, or even read code in PhpStorm.  Yes, I can navigate them, but to still adds cognitive load. 

Honestly, we are debating preferences here.  Can we focus on objective things?

> "Constants are defined at compile-time" is a status quo in the same sense as "variables can be assigned to" is a status quo - you could certainly design a language differently, but it's not just something that happened by accident, it's a design decision.

It was a design decision in that maybe it did not occur to them that it would be useful; IOW a design by omission.

Design decisions can be changed.  PHP 5 changed the design decisions of PHP objects and added the __set() method. Before then it could have easily been argued that variable accessed a memory location, but never called code. But then that design decision was changed.  

So there is no reason I've heard thus far that this one could not be changed too.

> An interesting exercise is to ask if there are other languages that take a different decision - it's not decisive, either way, but it can be suggestive.
> "Can constants be evolved into something that is initialised on-demand?" is not something I'm aware of examples of elsewhere. That could be because nobody's thought of it before, or just that I haven't come across it, but it could be because there's a better solution to the underlying requirement.

Excellent point.  

Ruby has the .replace method for constants and can thus assign at runtime:
=========
https://stackoverflow.com/a/6712438/102699

Javascript can assign constants at runtime:
=========
fn = function() { return "World"; };
const who = fn();
alert( "Hello " + who );

C++ has const member functions (not exactly the same, but similar):
=========
https://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter08_014.html

Python adds a final qualifier to turn variables into constants. Thus is supports initializing constants:
=========
https://stackoverflow.com/a/47821212/102699
http://code.activestate.com/recipes/65207-constants-in-python/

Java creates class constants with `public static final` which can be initialized with code:
=========
https://www.javaworld.com/article/2076481/use-constant-types-for-safer-and-cleaner-code.html

I think that is a fairly conclusive list of highly-used languages, all of which allow constants to be initialized with code. This list would imply any assertion that constants must be handled at compile time is not applicable for many other languages so I would argue to not a universal principle.

Also it would imply that allowing constants to be initialized via runtime code would not disrupt the time-space continuum, either.

> I think the syntax being the same when you *reference* the value bothers me more than how it is declared. If I see ClassName::USER_ID, I expect the value to be the same each time the code runs, and to be able to find the value directly in the source.

Again, I ask if that your unease has more to do with status quo bias vs. something fundamentally wrong with the concept?

> It's a limitation in the very basic sense that it limits what you can do. You can't write this, for instance:
> 
> class API {
>     private $config;
>     public function __construct($config) {
>         $this->config = $config;
>     }
> 
>     const URL {
>          return $this->config->getValue('API_URL');
>     }
> }

But you could do this:

class Api {
   const URL:string {
         return Config::get()->api_url;
    }
}

class Config {
   public $api_url;
   private static $_current;
   function __construct($args = array()){
      $this->api_url = $args['api_url'];
   }
   static function get():Config{
      if ( ! isset( self::$_current ) ) {
         trigger_error('must call Config::set() before calling Config::get()');
         `die()`;
      }
      return self::$_current;
   }
   static function set( Config $config ){
      self::$_current = $config;
   }
}

Config::set( new Config(['api_url' => 'https://api.example.com']) );

//...
$api_url = Api::URL;

Any way, why again must perfect be the enemy of the good?

> Laravel's "facades" are global variables hidden behind static methods. So you write "DB::query($sql)" as a short-hand for "DB::$currentInstance->query($sql)". Most of the time, you just treat it as static, but if you want to write tests, for instance, you can inject a mock as the current instance.

Ah. 

> An interesting thing is that you can evolve a class that really is static and replace it with one of these facades. Unfortunately, the same wouldn't be true with initialise-once constants, because once the constant had been read, you couldn't swap it out in another test.

Well, I am suggested one-time only because I assumed that one-time only _is_ the only thing that is fundamental to `const.`  But I can see uses for being able to redefine them — when running unit tests — but I was not planning to ask for that.

> Many people would say the kind of global state you're talking about is an anti-pattern.

Well, Larry did, which is reality is an appeal-to-authority argument and not an argument on the merits.  

But I used "anti-pattern" because I did not think I was trying to convince anyone against adding a pre-processor, so it was just easier to write!

> I'm just looking for solutions to your use case, which as I understand it is values which change rarely based on configuration.

I honestly do appreciate it.  You are generally very good about that kind of thing.

-Mike

---

## #107999 — tyson andre — 2020-01-05T22:04:34+00:00

URL: https://externals.io/message/108238#107999 · In reply to: #107961

I have a work in progress wrapper for allowing const-like syntax for global constants `static const X = $dynamicExpression;`.
This can be seen in https://github.com/TysonAndre/php-src/pull/10
This is basically a wrapper around if (!defined(...)) { define(...); }
If anyone's planning to implement changes to the way constants can be declared,
then the POC might be worth looking at (e.g. to see what parts of the code would need to be changed),
even if their design and approach is different.

- The issues this solves has overlap and differences with what Mike Schinkel requests here.
  It's intended as a solution to limitations in different use cases that I've encountered/thought about (which this thread reminded me of).

  Notably, this deliberately can't refer to other *dynamic* "static const" values that haven't been declared.
- This deliberately limits it to the top-level statements in files
- Class constants below the global 'static const' can then use the eagerly evaluated const.

Reasons for me to implement the proof of concept this way:
- `defined()`/define() is already supported by the engine.
  This is a slight improvement due to shorter syntax,
  and being more certain the const is only set or used in one file.
- Eagerly initialized dynamic constants are easier to reason about in some ways (e.g. fetching the constant of an already fetched class is less likely to cause pauses or exceptions.)
  This makes un-noticed errors in rare code paths less likely.
- Attempting to support function calls, property accesses, class constant accesses (e.g. inherited), and variables in class constants recursively and/or lazily
  would probably break a lot of code/assumptions in the php engine.

  I might be mistaken about that, though - I'd be glad if it turned out to be easy to implement.

Declaring 'static const' as a top level statement of a function/closure/method might also be possible, but would require more work.

------

It's also worth mentioning that some expressions that definitely have constant values
can't be written (in a convenient way) because they use syntax that is prevented right now, such as function calls, closures, etc.

My preference would be to make it easier to use dynamic expressions, and recommend in coding guidelines that
certain types of expressions "SHOULD NOT" be used for constants that vary on a particular host.
(network calls, time, etc)

```
const DEFAULT_PAYLOAD = json_encode(['key' => 'value']);
const ALLOWED_ELEMENT_TYPES = array_merge(self::FIRST_TYPES, self::SECOND_TYPES);
const ELEMENT_LENGTHS = array_map('strlen', self::ELEMENTS);
```



> For derive-once global constants, you may be looking for define.
> That's a runtime operation and effectively boils down to a set-once global.
> It doesn't help with constants defined in a class,
> but for an already-global constant define already gives you a one-time set operation,
> just not on-demand.

This POC adds an alternate syntax for a set-once global.
It prevents accidentally reusing the same global name in multiple files (e.g. with copy and paste),
and lets people looking at it know the constant is only used in that file.

> Another use-case that just occurred to me is to support testing:
> 
> class Api {
>   const URL: string{
>     return $_ENV['EXAMPLE_API_URL'] ?? 'https://api.example.com';
>   }
> }

That example would be written with the POC as:

static const API_URL = $_ENV['EXAMPLE_API_URL'] ?? 'https://api.example.com';

class Api {
  const URL = API_URL;
}

---

## #108005 — Robert Hickman — 2020-01-06T10:56:58+00:00

URL: https://externals.io/message/108238#108005 · In reply to: #107999

Would it be worth expanding the ideas of programmatic constant
definition into a more general compile-time code execution approach?
It would work well with preloading introduced in 7.4, and could allow
some of the things frameworks are currently doing at runtime to be
done once at compile time (opcode generation time). The
work-in-progress language JAI has a very interesting compile time
execution system which could possibly be a source of inspiration.

---

## #108008 — Larry Garfield — 2020-01-06T18:07:32+00:00

URL: https://externals.io/message/108238#108008 · In reply to: #108005

Lots to reply to here, so I'm going to munge it into a single response.


Mike Schinkel said:

> Let's consider the fact that is PHP had never had the `define()` and instead implemented the `const` keyword from the start, and only allowed compile time values. If PHP had done that, you would be arguing from a status quo bias the same thing about making that dynamic. And it currently is dynamic which IMO invalidates your argument that constants must be defined at compile time.

Do not put words in my mouth and presume to know what I would think in some hypothetical situation.  That is rude and inappropriate.

You also repeatedly are accusing Rowan and I of "status quo bias" as though it were a bad thing.  Let me be clear: I entirely agree with Zeev that in a language used by millions of people a "status quo bias" is a *very good thing*.  The potential benefit of any improvement needs to substantially outweight any downsides, and the burden of proof is on the person/people proposing a change.  There's a long list of things I'd like to see added to the language myself, but I'm well aware that the burden of proof is always on the proposer (in this case, you), as it should be.

Also Mike:

> > […]

> Well, Larry did, which is reality is an appeal-to-authority argument and not an argument on the merits.  

So you call something an anti-pattern, then get on my case for "appeal to authority" for calling something an anti-pattern?  Your argument style needs work, dude.

Global data is well-recognized across the industry as dangerous, and the larger the system the more dangerous it is.  It violates pure functions, and pure functions are how you get any semblance of predictability in your code.  I do not have the time or inclination to go through all the details of that here; you can find ample resources for that yourself.

(And for the record, "appeal to authority" is only a logical fallacy if the authority being appealed to is not a qualified authority on the topic at hand.  Appealing to Tiger Woods's authority on golf clubs is not a logical fallacy because he really would have more knowledge and experience than most; appealing to his authority on baseball gloves would be a logical fallacy.)

> I take it you have never used Pantheon hosting then?  You create a named environment to match a Git branch.

Full disclosure: I work for Platform.sh, which has been doing environment-per-bit-branch longer than Pantheon has.  I'm responsible for maintaining our public utility libraries for bridging environment variables into application configuration for environment-sensitive things like DB credentials and route names.  So I am very intimately familiar with this problem space, probably moreso than most people. :-)

In my experience, systems that configure themselves via constants are the worst.  I know some do, but it's the most annoying option to work with.  It makes any sort of dynamic override (which you *must* do in a cloud hosting enviroment) a PITA.  The best option are systems that are configured by env vars, and have a very clearly set of documented env vars to populate.  Env vars are also globally readable but you're not tricked into thinking they're compiled out at compilation time (as things with `const` are.)  They're also by far the easiest for me to work with as the person writing that glue code.

Adding language features that make it easier for people to take the approach that is in my experience the most painful option is something I am not going to support. :-)


To the use cases, "I'm changing the API but don't want to change the API" (what you're calling evolving here) is not an argument that's going to carry weight with me.  It's not a use case I feel is worth the confusion that comes with "Sometimes a constant is not a constant".  The links you offer offer some other potential use cases that are worth considering, but "I made something a const when I shouldn't have" is not going to be convincing to me.

Enums are also not constants.  They're sum types.  They can be implemented using a collection of constants, but that's a mediocre implementation of them (even if a popular one).


To the list of links Mike offered, let me add Wikipedia:

https://en.wikipedia.org/wiki/Constant_(computer_programming)

So, to examine the full problem space using Wikipedia's terminology:

1) Macro constants.  These are handled at compile time and do not exist at runtime.  This is what PHP's const is currently, both global and class-based.

2) Dynamically valued constants.  This is what Java and C++ have, and essentially what Javascript's const does.  This supports dynamic code that runs a computation to produce a value, once.  That value's scope is based on whatever that variable's scope would be otherwise.  (Could be global, could be class static, could be local to a function.)  It's unclear when that code is actually run, and likely varies by language; also, it's unclear if it's legal to have the computing code depend on runtime information.  That may also vary by language; I'm not sure.

PHP's `declare` statement is more akin to the latter, albeit hoisted to global scope.  It's not computed on-demand but whenever the `declare` line happens to be reached.  There is no class-level equivalent.

Unlike the other languages listed, PHP also has a syntactic split between variables and constants.  Variables have a $, constants do not.  Whether that's good or bad is an academic question as it's clearly not going to change.  That's in contrast to most other languages where there is no syntactic distinction, just the convention of capitalizing not-gonna-change things.

Dynamically valued constants are... I'll say similar to but not quite the same as an auto-memoized function.  For one there's the () vs not; for another, an auto-memoized function may take parameters and thus memoize different return values.  From one perspective one could look at dynamically valued constants as a zero parameter self-memoizing function with some syntactic sugar.


I can see a use for const variables, especially const class variables.  The example in the Java link Mike provided is actually something I ran into just last week and it would have been very nice to have that ability.  However, class constants in PHP right now are limited to Macro constants.

So the more general request here is for dynamically valued constants beyond the current `declare` support.  Which... I could potentially get behind.  Self-memoizing functions is one way they could be implemented, if you don't mind ().  (I don't.)  That would also have a lot of other benefits.

The caveat in both cases is dependencies.  Memoizing an impure function can lead to all sorts of time-dependent silliness.  The same concern applies to dynamically valued constants.  If their generation code is a pure function (however expressed), then cool, that's a safe and nice feature.  If it has unpredictable dependencies, though, the behavior can be equally unpredicatable.  That includes depending on $_GET or env vars.  (Remember, PHP has plenty of users outside of shred-nothing requests, and with FFI and preloading hopefully more of them wil get used more often.)

So I could get behind dynamically valued constants and/or auto-memoizing functions.  That would probably mean they still have a $ on them, which doesn't bother me.  That clearly separates them visually from macro constants, which I like.

The concern is when you use impure functions to generate them, the results are unpredicatable and, depending on the circumstances, may be non-deterministic.  The potential for hard to find bugs here is high; although the counter point is that using a function/method to emulate them today, which you can absolutely do, offers the exact same risk.  So I am nervous here but not quite agahst, since... "status quo bias". ;-)

There's a lot of ways that could be done syntactically.  One option is Tyson's proposal, although I would prefer to not pollute `const` with it and use a declare, like so:

// This line already works.
declare('API_URL', $_ENV['EXAMPLE_API_URL'] ?? 'https://api.example.com');

// The change would just be allowing the const to lazy-populate here.
class Api {
  const URL = API_URL;
}

Another option is to allow `declare` in classes:

class Api {

  declare string $URL = $_ENV['EXAMPLE_API_URL'] ?? 'https://api.example.com');

}

(Or something like that.)

The trick is when exactly these run, because they're impure functions so whether they run at code compile time or first-access could mean a dramatic difference in their resulting value.  And that is precisely why I am very, very nervous about allowing impure functions to be cached, whatever the syntax on top of them.  (Constant-like or not.)

---

## #108011 — Mike Schinkel — 2020-01-06T19:17:10+00:00

URL: https://externals.io/message/108238#108011 · In reply to: #107999

> 
> 
> I have a work in progress wrapper for allowing const-like syntax for global constants `static const X = $dynamicExpression;`.
> This can be seen in https://github.com/TysonAndre/php-src/pull/10

Thanks for replying and including this.  Nice work.


> - Eagerly initialized dynamic constants are easier to reason about in some ways (e.g. fetching the constant of an already fetched class is less likely to cause pauses or exceptions.)
>  This makes un-noticed errors in rare code paths less likely.
> - Attempting to support function calls, property accesses, class constant accesses (e.g. inherited), and variables in class constants recursively and/or lazily
>  would probably break a lot of code/assumptions in the php engine.

Frankly my strong preference would be for on-demand initialization because otherwise each page load could end up running lots of code to calculate values that are never used on the particular page.

> My preference would be to make it easier to use dynamic expressions, and recommend in coding guidelines that
> certain types of expressions "SHOULD NOT" be used for constants that vary on a particular host.
> (network calls, time, etc)
> 
> ```
> const DEFAULT_PAYLOAD = json_encode(['key' => 'value']);
> const ALLOWED_ELEMENT_TYPES = array_merge(self::FIRST_TYPES, self::SECOND_TYPES);
> const ELEMENT_LENGTHS = array_map('strlen', self::ELEMENTS);
> ```

You and me both.

-Mike

---

## #108012 — Mike Schinkel — 2020-01-06T20:28:56+00:00

URL: https://externals.io/message/108238#108012 · In reply to: #108008

> 
> 
> Do not put words in my mouth and presume to know what I would think in some hypothetical situation.  That is rude and inappropriate.

I do not mean to nor want to start any conflict here. I know I felt some of your phrasing rude and so I was probably writing in frustration. Hopefully we can both moderate as conflict is not good for any of us.

> You also repeatedly are accusing Rowan and I of "status quo bias" as though it were a bad thing.  Let me be clear: I entirely agree with Zeev that in a language used by millions of people a "status quo bias" is a *very good thing*.  

You are misunderstanding the definition of "status quo bias."  Here is part of the definition:  

"Status quo bias is an emotional bias. The current baseline (or status quo) is taken as a reference point, and any change from that baseline is perceived as a loss. Status quo bias should be distinguished from a rational preference for the status quo ante, as when the current state of affairs is objectively superior to the available alternatives, or when imperfect information is a significant problem."  

When I used that phrase I was specifically referring to the emotional reaction vs. the rational reaction. I understand and agree when rational arguments are used, but lately in our political climate people seem to often to start with emotional arguments. And calling something an anti-pattern or the alternative a "best practice" is not a rational argument; both of those are loaded words.

From: https://en.wikipedia.org/wiki/Status_quo_bias

But if I mischaracterized your objections I apologize.  

> So you call something an anti-pattern, then get on my case for "appeal to authority" for calling something an anti-pattern?  Your argument style needs work, dude.

I notice you did not quote my full statement. I explicitly called out the irony of my own usage and made the distinction that I was not using the phrase to argue against someone else's proposal which is why I felt ok to use it, because I was not using it to block anyone else's needs. 

Again, my goal is not conflict but hopefully improving PHP.

> Global data is well-recognized across the industry as dangerous, and the larger the system the more dangerous it is.

Eh, not exactly.  Global data that is *mutable* is bad.  Immutable global data — especially constants — are not necessarily bad, at least per the highest voted answer which means the topic is definitely debatable:

https://stackoverflow.com/a/1265684/102699

And a constant that is initialized at runtime is still immutable through the life of the execution.

>  It violates pure functions, and pure functions are how you get any semblance of predictability in your code. I do not have the time or inclination to go through all the details of that here; you can find ample resources for that yourself.

I am very familiar with pure functions, you don't need to go through the detail with me.

That said, class methods in PHP are not pure functions and PHP has not embraced pure functions so I am not sure why you are using this your rational other than maybe you wish PHP was a pure-function language?   Of course if it was limited to pure functions, we'd not be able to configuration from outside the program anyway.

> (And for the record, "appeal to authority" is only a logical fallacy if the authority being appealed to is not a qualified authority on the topic at hand.  Appealing to Tiger Woods's authority on golf clubs is not a logical fallacy because he really would have more knowledge and experience than most; appealing to his authority on baseball gloves would be a logical fallacy.)

When you said it was an anti-pattern you used passive voice and did not include a reference to any authority.

That said, I have been programming professionally for 30 years now and I have seen many best practices from "authorities" be promoted, and then later seen a tidal wave of developer sentiment argue against those same best practices, i.e. inheritance and then containment, as one example.

I've also seen authorities promote an idea and then watched as developers latched onto it and give it a life of its own, far outpacing what the authority orginally intended, e.g. Bob Martin and the single responsibility principle.

In my experience "best practice" and "anti-patterns" are too often used in debate even when they do not actually apply to the use-case, so I prefer to dispense with those words when trying to debate actual pros and cons of an approach.

> I work for Platform.sh, which has been doing environment-per-bit-branch longer than Pantheon has.  

That statement was in response to Rowan's comment, not yours.

> In my experience, systems that configure themselves via constants are the worst.  

Of course they are the worst! Because you can't initialize constants dynamically. 

That is exactly what I am trying to change.

> It makes any sort of dynamic override (which you *must* do in a cloud hosting environment) a PITA.  

Exactly.

> The best option are systems that are configured by env vars, and have a very clearly set of documented env vars to populate.  Env vars are also globally readable but you're not tricked into thinking they're compiled out at compilation time (as things with `const` are.)  They're also by far the easiest for me to work with as the person writing that glue code.

The word "tricked" is a pejorative and not helpful for our discussion.  

What I read here is that you have a preference for using Env vars directly and I have a preference for encapsulating them into constants.  I don't see that either of us are more "right" than the other, we just have different preferences.

I still have not heard a real reason why Api::URL is somehow worse to use than $config->api_url() other than your claim that the former is "global" and thus bad, which I challenge because the former would be immutable.  This is one of the areas where I think the "anti-pattern" does not apply. 


> Adding language features that make it easier for people to take the approach that is in my experience the most painful option is something I am not going to support. :-)

Have you worked with other languages besides PHP where dynamically initialized constants were possible, and this caused problems?

> To the use cases, "I'm changing the API but don't want to change the API" (what you're calling evolving here)

As you said above that it was rude to put words in your mouth, it is also rude to keep changing my words to better fit your argument. Let us both not do that.

>  "Sometimes a constant is not a constant".  

What I proposed will always be a constant.  Again, it would be immutable once set.  The fact is it set at compile time does not make it constant, per the lin

> From one perspective one could look at dynamically valued constants as a zero parameter self-memoizing function with some syntactic sugar.

Yes.

Although I tend to avoid using the term "memoizing" because most people I know have to look it up and then ponder over its meaning.

> I can see a use for const variables, especially const class variables.  

I assume you mean immutable variables that still use the $, like 'let' in Javascript?   

Those would definitely be very useful, but unfortunately would not address the use-case I presented.

> So the more general request here is for dynamically valued constants beyond the current `declare` support.  Which... I could potentially get behind.  Self-memoizing functions is one way they could be implemented, if you don't mind ().  (I don't.)  That would also have a lot of other benefits.

I am confused. This sounds like you are now agreeing with the need for dynamic constants?

BTW, the requirement for () means it would not address the use-case of allow code to be evolved.

> The caveat in both cases is dependencies.  Memoizing an impure function can lead to all sorts of time-dependent silliness.  The same concern applies to dynamically valued constants.  If their generation code is a pure function (however expressed), then cool, that's a safe and nice feature.  If it has unpredictable dependencies, though, the behavior can be equally unpredicatable.  That includes depending on $_GET or env vars. 

Totally agree that people could write bad code. But there are a thousand ways they can already write bad code.  

If a dynamic constant were to use $_GET or $_ENV it could be problematic, but it could also be coding in a manner than it would be completely robust, and making it robust is not hard.

>  (Remember, PHP has plenty of users outside of shred-nothing requests, and with FFI and preloading hopefully more of them wil get used more often.)

Maybe there is something here I am missing? Can you present a problem that dynamic initialization of a constant would create only a statically defined constant in these cases.  I cannot think of one, but I will be honest and admit that does not mean there is not one.

> So I could get behind dynamically valued constants and/or auto-memoizing functions.  That would probably mean they still have a $ on them, which doesn't bother me.  That clearly separates them visually from macro constants, which I like.

Having the $ requirement blocks my primary use-case, so it would bother me.

> The potential for hard to find bugs here is high; although the counter point is that using a function/method to emulate them today, which you can absolutely do, offers the exact same risk.  

And that's the key point. It really is no different from what we have today, except that what I proposed would provide evolvability.

> The trick is when exactly these run, because they're impure functions so whether they run at code compile time or first-access could mean a dramatic difference in their resulting value.  And that is precisely why I am very, very nervous about allowing impure functions to be cached, whatever the syntax on top of them.  (Constant-like or not.)

When you say "cached," what are you thinking the lifetime would be?  And how would that really be different from a static constant?  

Are you concerned that the code could fail and leave the constant in an invalid state?  

A potential way to address that is for a dynamic constant to require type hinting, and thus if it did not return the correct type it would fail with an easy to trace error output. And also disallow throwing errors outside of the constant. That would force developers to write robust dynamic constant code.

-Mike

---

## #108013 — Mike Schinkel — 2020-01-06T21:01:38+00:00

URL: https://externals.io/message/108238#108013 · In reply to: #108005

> 
> 
> Would it be worth expanding the ideas of programmatic constant
> definition into a more general compile-time code execution approach?
> It would work well with preloading introduced in 7.4, and could allow
> some of the things frameworks are currently doing at runtime to be
> done once at compile time (opcode generation time). The
> work-in-progress language JAI has a very interesting compile time
> execution system which could possibly be a source of inspiration.

That would certainly be interesting.  I do not know enough what is possible there to opine.

It it were possible that might be the best of all worlds.  

Anyone with experience in that area able to comment?

-Mike

---

## #108014 — Larry Garfield — 2020-01-06T21:24:07+00:00

URL: https://externals.io/message/108238#108014 · In reply to: #108012

> > […]

*snip*

> From: https://en.wikipedia.org/wiki/Status_quo_bias
> 
> But if I mischaracterized your objections I apologize.  

I am not anti-change for anti-change's sake.  Since I presume from earlier comments that you're familiar with my background in Drupal I think that is sufficient evidence that I am not against change as a concept. :-)

Let us move on.

> > […]
> 
> I am very familiar with pure functions, you don't need to go through 
> the detail with me.
> 
> That said, class methods in PHP are not pure functions and PHP has not 
> embraced pure functions so I am not sure why you are using this your 
> rational other than maybe you wish PHP was a pure-function language?   
> Of course if it was limited to pure functions, we'd not be able to 
> configuration from outside the program anyway.

Yes, PHP doesn't have pure functions as an explicit thing.  This makes me sad. :-)  The point here is that when I call a function in PHP, I intuitively expect that it may not be pure, or may be context-sensitive.  That may be desireable or not in context, but the () indicate "code will run, and code may do weird stuff".  When I see a constant, I expect it to be, well, constant, deterministic, and have a small fixed performance impact.  (In the case of `const`, zero performance impact.)

For example:

const DEBUG_MODE = false;

function foo() {
  if (DEBUG_MODE) {
    // Do stuff.
  }
}

PHP will currently (AIUI) inline the DEBUG_MODE value at compile time, notice that the code path is unreachable, and omit it from the compiled opcodes entirely.  (I'm pretty sure it does that now, at least; if not, it certainly can be.)

If you use a define, however:

define(DEBUG_MODE, $_ENV['debug'] ?? false);

function foo() {
  if (DEBUG_MODE) {
    // Do stuff.
  }
}

That optimization cannot happen.  But just from looking at foo() I cannot tell which is going to happen.  I view that as a negative.


> > […]
> 
> That statement was in response to Rowan's comment, not yours.

I know; I included that as a resume so that my following points would carry more experiential weight.  (And because we really were doing it before Pantheon, who is a competitor of ours, so there. :-) )

> > […]
> 
> Of course they are the worst! Because you can't initialize constants 
> dynamically. 

No, it's because you cannot override them.  It makes the conditional logic around defining them more complicated.  Specifically, with WordPress for example, I cannot just leave WP's default config script in place and tack ours on before/after it.  I have to hack theirs up and replace it with one that checks every possible source for DB configuration information and then set it once, because it cannot then be overridden.  They actually *are* set dynamically with declare().  The runtime dynamism isn't the issue.  In this case it's actually the immutability that causes the problem, ironically enough. :-P

An example of where I can see problems: In some systems, $_GET is treated as mutable.  It is, but modifying it is generally a bad idea, but people do it anyway.  For instance, I've seen "clean URLs" implemented by modifying the $_GET variable and $_SERVER variable to mutate example.com/foo/bar into example.com/index.php?q=foo/bar.  (Drupal 7 for instance did that.)

Now suppose you have a lazy-loading "constant" that uses $_GET.  If it gets called before that conversion happens in some code paths but not others, its value will be unpredicatable.  It may get built before $_GET['q'] is changed or after, which would change its value.

That's not something I ever expect a "constant" to do.  I expect a constant to be constant from one request to another, even if it nominally isn't derived until runtime.  Constants that change value are not constant.

(Is this a subjective and partially emotional interpretation?  Of course it is.  Much of language design, like any other design, is.  But I don't think my subjective view here is unique.)


> > […]
> 
> I assume you mean immutable variables that still use the $, like 'let' 
> in Javascript?   

Yes.  Or the static class property example with color from the Java URL you provided, which is a nice use case.

> Those would definitely be very useful, but unfortunately would not 
> address the use-case I presented.

The specific use case you presented is not one I believe PHP should support.  If I have not convinced you of my position on that yet, I don't think I am going to, nor are you going to convince me of yours.

> > […]
> 
> I am confused. This sounds like you are now agreeing with the need for 
> dynamic constants?

I am thinking aloud and allowing the available options to go where they logically go.

> BTW, the requirement for () means it would not address the use-case of 
> allow code to be evolved.
> 
> > […]
> 
> Totally agree that people could write bad code. But there are a 
> thousand ways they can already write bad code.  

Certainly.  But a good language nudges people toward writing good code, or at least not-bad code.  (How hard it should nudge is a subject of much debate and is one of the reasons we have many languages.)

> > […]
> 
> Maybe there is something here I am missing? Can you present a problem 
> that dynamic initialization of a constant would create only a 
> statically defined constant in these cases.  I cannot think of one, but 
> I will be honest and admit that does not mean there is not one.

See the $_GET['q'] example above for the sort of unpredictability I am concerned about.  In a long-running process the potential for such issues is higher.

> > […]
> 
> When you say "cached," what are you thinking the lifetime would be?  
> And how would that really be different from a static constant?  

Any dynamic constant variable has a lifetime of its scope.  If global, then it's the lifetime of the process.  

> Are you concerned that the code could fail and leave the constant in an 
> invalid state? 

That wasn't what I was thinking of, although I suppose that's also a concern.  I was more thinking of the inconsistent initialization time, as in my earlier example, leading to a constant that is variable request-to-request even if it's nominally constant-once-set within a specific request.  That's a very different behavioral pattern from guaranteed consistent request-to-request.  IMO they should remain separate.

--Larry Garfield

---

## #108015 — Rowan Tommins — 2020-01-06T21:59:29+00:00

URL: https://externals.io/message/108238#108015 · In reply to: #107998

> Yet you called my list of URLs enums, and they are not at all arbitrary and absolutely want to change.


I apologise - I took a guess without context for how those constants 
would be used, but apparently they're something rather different.


> I take it you have never used Pantheon hosting then?  You create a named environment to match a Git branch.


I'm still struggling to understand why you need multiple constants, all 
mutable based on some other dimension. If the ultimate goal is to know 
the name and branch of the *current* environment, how do the constants help?

It seems on the face of it like the constants have outlived their 
usefulness, and need to be replaced by a lookup table of some kind.


> What is "better" in this context is one's opinion. You might not find 
> it beneficial, but I would. 


Agreed. I actually said the same in one draft reply, but maybe deleted 
it while editing down.


> > […]
> It was a design decision in that maybe it did not occur to them that it would be useful; IOW a design by omission.


We'll just have to agree to disagree here. I stand by the assertion that 
"compile-time constants" are a well-defined feature, whose restrictions 
are part of their definition and purpose.


> Ruby has the .replace method for constants and can thus assign at runtime:
> =========
> https://stackoverflow.com/a/6712438/102699


That method is not actually a feature of constants, its abusing the fact 
that everything is an object, so the *value* of a constant might still 
be a mutable object. It's an interesting "cheat", but it's not really a 
deliberate language feature.


> C++ has const member functions (not exactly the same, but similar):
> =========
> https://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter08_014.html


Unless I'm missing something, this isn't really related to anything 
we've been discussing, it just happens to use the keyword "const".


> Python adds a final qualifier to turn variables into constants. Thus is supports initializing constants:
> =========
> https://stackoverflow.com/a/47821212/102699
> http://code.activestate.com/recipes/65207-constants-in-python/


Those links seem to say that Python doesn't have constants *at all*, but 
you can emulate them in various ways. As such, you can give them pretty 
much whatever behaviour you like, since you're defining the language 
feature from scratch.


> Javascript can assign constants at runtime:
> =========
> fn = function() { return "World"; };
> const who = fn();
> alert( "Hello " + who );

This is a more interesting case; some properties to ponder:

- implemented as a special kind of variable (e.g. they have the same 
scope rules and syntax as "let" variables)
- can be global or block-scoped, but not a property of an object
- cannot be pre-declared before they are assigned, or automatically 
initialised on first reference
- can contain any value, including mutable objects


> Java creates class constants with `public static final` which can be initialized with code:
> =========
> https://www.javaworld.com/article/2076481/use-constant-types-for-safer-and-cleaner-code.html


Another interesting case:

- implemented as a special kind of variable
- can contain any value, including mutable objects
- will raise an error if the compiler cannot prove there is exactly one 
assignment; i.e. these are not purely run-time constructs like PHP's 
 `define()` constants
- can be static members of a class, and include an inline initializer
- can be static members of a class and have no inline initializer, in 
which case they *must* be initialized using a "static initializer block" 
which will be run the first time *the class* is referenced at run time 
(a bit like substituting the value during autoloading in PHP)
- can be instance fields, and must be initialised inline, or in every 
possible constructor
- can be block-scoped variables, and must be initialised before first use

The interesting thing here is that the constants are initialised the 
same way as any other variable, not using any special on-demand code. 
Because of the language's design, the compiler is able to prove that 
they are assigned exactly once. That proof is probably a lot harder in 
PHP, and the check would probably be punted to run-time.


The biggest sticking point in my mind is your insistence that they share 
syntax with constants, rather than variables. This means they can only 
be used in static class or namespace context, not against an instance.

It also means *widening* the meaning of a construct, rather than 
*narrowing* it: the "final" keyword in Java doesn't let variables do 
anything new, it just restricts them; your proposed constant 
initialization would allow constants to act in new ways, including 
holding complex, mutable types. That's likely to cause confusion for 
both humans and tools written based on the current concept that a 
constant can always be computed at compile-time.



> > […]
> But you could do this:
>
> class Api {
>     const URL:string {
>           return Config::get()->api_url;
>      }
> }


The difference is that a Java-style "final variable" can have a single 
value *per instance*, so this is valid:

class API {
     private final $URL;
     public function __construct($config) {
         $this->URL = $this->config->getValue('API_URL');
     }
}



> > […]
> I honestly do appreciate it.  You are generally very good about that kind of thing.


I would be interested to hear more about why you think pre-processing 
doesn't suit your use case. It seems like a good fit to me:

- naturally fits with code expecting compile-time constants
- no run-time overhead if done as part of deployment
- can be implemented using succinct syntax, rather than run-time 
initialisation code


Regards,

-- 
Rowan Tommins (né Collins)
[IMSoP]

---

Next page: https://externals.io/message/108238.md?page=2
