> On Jan 3, 2020, at 2:52 PM, Larry Garfield <la...@garfieldtech.com> wrote:
> 
> 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

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to