> On Jan 3, 2020, at 2:52 PM, Larry Garfield <[email protected]> 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