> 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