# Exakat > Bring Quality to your PHP Projects - Brand: PHP, static analysis --- # About us Source: https://www.exakat.io/about-us/ We are passionate about Web technologies, computers and innovation. We like to go where no one else will.  We work for you : we'll listen, think hard and will make machines and applications works even harder for you. We are open, friendly and always fond of sharing experience and knowledge. --- # Scratchy, the Universal Elephpant Source: https://www.exakat.io/scratchy-the-universal-elephpant/ Scratchy - The Universal Elephpant - Scratchy, the Universal Elephpant The most versatile PHP companion you'll ever own! [Pre-order Now a Box of 50**](https://www.exakat.io/product/scratchy-elephpant-order/) ![Scratchy the Elephpant](https://www.exakat.io/wp-content/uploads/2025/09/Scratchy-Front.jpeg) ## What is Scratchy? ![Elephpant with patch](https://www.exakat.io/wp-content/uploads/2025/09/Scratchy-SideScratch.jpeg) It's an elePHPant, like any other: easily recognizable, sturdy, lovable, safe with children and developers, yet proficient in PHP (almost), and blue. It has the PHP letters on the left side, as usual. It is also versatile: on the right side, there is a removable patch like a velcro patch, that may be changed, and uses to hang your choice of messages. Get Yours for €30 in March 2026 ** ## Why the Universal Elephpant? ** ### Versatility With the removable patch, the same elephpant is now available for multiple occasions. Keep it pristine, and it is a nicely behaving elephpant, ready to code PHP. Change the patch, and you can now dress it for any sorts of occasions. ** ### For Smaller Communities Scratchy is here to help smaller communities! Everyone can get their own elephpant, and then communities provide the patches. The patches may be ordered locally in any reasonable batches. ** ### Wider Exchanges With extra patches, make friends across the PHP planet! Get in touch with people celebrating their achievements and swap patches! Patches are simple to ship by mail, cheaper and better for the environment. ** ### Where to Get It Scratchy is produced in large batches and available for purchase from Exakat in boxes of 50. The first time, the elephpant comes with a patch, then you won't need it anymore. [Join the Community - Pre-Order Now your Box of 50](https://www.exakat.io/product/scratchy-elephpant-order/) ## How to Make Your Own Patches ### Patch Creation Guide ** Find a local provider for your patches - ** Define your design - use your logo, project name, or community - ** Choose quality and quantity - prices range from €0.8 to €3.5 per patch - ** Make sure the patch covers the support dimensions (TBD) - ** Keep the message welcoming and friendly, just like PHP and its community ### The Patch Museum To help collectors across the world, we'll keep a list of all created patches. When you get your patch, send us 3 copies to our address and we'll put them on the Museum of Elephpant Patches (MEP) website, along with your community info. No elephpant yet? No problem! You don't need the elephpant to make the patch. Just create your patch and ship it to PHPamilies as you like. You can always get the elephpant later. Learn More About Patches Soon here ## Pricing Options ### Wholesale €30 each - Box of 50 elephpants - + shipping costs - + VAT if applicable - + Financal fees if applicable for payment by wire transfer - Neutral tag PHP included - Perfect for communities - Room for resell to fund your group [Order Wholesale ](https://www.exakat.io/product/scratchy-elephpant-order/) POPULAR ### Individual Price to be defined later each - Single elephpant - + shipping costs - + VAT if applicable - Perfect for developers - Limit of 3 per order - For those far from communities Order in March 2026 ## Project Timeline ** ### Design Phase Artistic creation by Vincent Pontier and Damien Seguy ** ### Prototype Validation Testing of early patches and some teasing ** ### Crowd Funding Start in September 2025 ** ### Production & Delivery Expected end of 2025 ** ### Birthday Party! Big patches party to celebrate Scratchy's arrival [Support the Project - Pre-order Now your Box ](https://www.exakat.io/product/scratchy-elephpant-order/) Scratchy, the Universal Elephpant [A project by Exakat SAS ](https://www.exakat.io/product/scratchy-elephpant-order/) © 2025 Scratchy Project. All PHP rights reserved. [ ** Pre-order your Box ](https://www.exakat.io/product/scratchy-elephpant-order/) --- # Price & Services Source: https://www.exakat.io/price-services/ --- # Bring Quality to your PHP code Source: https://www.exakat.io/ --- # Analytics on your code Source: https://www.exakat.io/leverage-on-methodology/ --- # Detect Tricky or Security Issues Source: https://www.exakat.io/detect-tricky-issues/ --- # Fix Automatically Code Source: https://www.exakat.io/fix-automatically-code/ --- # Public Analyzes Source: https://www.exakat.io/free-trial/ --- # Community Source: https://www.exakat.io/developers/ --- # Exakat Changelog Source: https://www.exakat.io/exakat-changelog/ ## Version 2.6.8 2024-05-02 - Fang Xuanling + Updated analysis : Undefined Enum Case now handles class constant relays - Report Ambassador : upgraded manual rendering - Analysis ## Version 2.6.7 2024-03-21 - Zhang Gongjin + New analysis : new rule for Deprecated attribute (analysis) - Analysis ## Version 2.6.6 2024-03-14 - Gao Shilian + New analysis : report usage of strpos() < 1 (possible bug) - Report Ambassador : fixed documentation display of PHP scripts - CallGraph : displays the call graph in dot format - Analysis - Tokenizer Made property inside a string with a Name, not a Identifier - Mark variable in append as modified ## Version 2.6.5 2024-01-31 - Cheng Yaojin + New analysis : dump all combined method calls - Architecture - Cobbler - Report - Analysis - Tokenizer Fixed display of ?-> inside strings - Refactored Goto labels with a common atom between goto and labels - Fixed minor errors with SEQUENCE (via NEXT) ## - + New analysis: report literal passed by reference - Architecture Moved assert configuration to ini_set and php.ini - Added a set of token values for Debian 12 and 8.3 - Void is now a single atom in the graph (speed up, less resources) - Speed up Load with less arrays, more classes - Analysis - Tokenizer Added CALLED link to new calls - Fixed edgecases with match and readonly ## Version 2.6.3 2023-12-14 - Ma Sanbao + Updated analysis : Too Many Variables in Method - Analysis - Tokenizer Fixed bug with short assignment left operand not being marked as read as well as written - Added fullnspath to Staticclass atom - Added support for THROWN, CALLED, YIELDED links in methods ## Version 2.6.2 2023-11-21 - Duan Zhixian - Analysis New analysis : Casting Method Favorite - Updated analysis : Ellipsis detection improved - New analysis : report arrays that are used for append and direct index access at the same time - New analysis : report get_class() and get_parent_class() without arguments - Updated analysis : Literal inventory now reports float, array() and heredocs - New analysis : report usage of advanced static variable initialisation - New analysis : cannot be readonly - New analysis : report triplet stats from the internal graph - New analysis : report static variables outside a method - Updated analysis : Missing types are now covering class constants too - New analysis : report usage of Deprecated features (CITE, functions, parameters...) - Updated analysis : Could Be Typed * now supports class constants - New analysis : add support for #[Override] before PHP 8.3 - New analysis : report variables that use their type as name ## Version 2.6.1 2023-10-19 - Liu Hongji - Cobbler New Cobbler : Logical to In_array() conversion - Analysis Updated analysis : Use same types for comparisons was refactored - Updated analysis : Add Zero skips ?? and ?: when it is used to create default values - Updated analysis : Implode() args order was refactored with type support - New analysis : report multiline expressions - New analysis : report usage of typed constants - Updated analysis : sprintf() argument counts is improved - Updated analysis : double instruction skips try, while, do while. - Updated analysis : useless instruction refactored clone expressions - Updated analysis : array Append in a list() call - Updated analysis : written only variables now take into account isset() too - Updated analysis : recursive functions don't report recursion via property or method call() - Updated analysis : Shell favorite ## Version 2.6.0 2023-10-04 - Xue Rengui - Architecture Refactored generation of VCS - Cobbler New cobbler : rename namespace - New cobbler : rename function - New cobbler : rename constant - New cobbler : rename class - New cobbler : rename interface - New cobbler : rename enums - New cobbler : rename trait - New cobbler : rename method - New cobbler : rename class constant - New cobbler : rename property - Report Added Classes dependencies table to Ambassador - Added Classes dependencies counts table to Ambassador - Added Classes dependent counts table to Ambassador - Added Namespaces to Exception tree - Added list of repeated class names - New report : Naming, that checks spelling - Analysis Updated analysis : Useless Null Coalesce now omits stdclass - New analysis : report rewritten final class constant - New analysis : report uselessly rewriten class constant - Updated analysis : Fixed detection of use for functions and constants - Removed analysis : Removed 'Mark callable' - Updated analysis : Fixed detection of calls to __construct - Updated analysis : Avoid Boolean as Argument sped up - Updated analysis : Property Could Be Local sped up - New analysis : Report blind variable used beyond their foreach() loop - Updated analysis : Could Use Try has more exceptions sources - New analysis : Report recalled conditions - Updated analysis : Upgraded Classes depencencies list with attributes, New initializers and instanceof - New analysis : Report incompatible property definition between trait and class - Updated analysis : Deep definition now includes define() calls and enums - Updated analysis : Collection of File dependencies now include interfaces - Updated analysis : Fixed but in Could Be Spaceship - Updated analysis : Upgraded 'unthrown exception' to handle variables - New analysis : report usage of self:: on - New analysis : report usage of DNF - Updated analysis : readonly usage covers classes and anonymous classes - New analysis : report usage of FTN as standalone type - New analysis : Collect usage of throw and their method - New analysis : Collect literals used in comparisons - New analysis : Suggest using array_combine() - New analysis : Report comparisons with distinct scalar types - New analysis : reports null being used as array's index - New analysis : collect all named things in the source code - Updated analysis : isComponent also supports enum and declare - New analysis : report useless Try clauses - New analysis : report converted exceptions - New analysis : report methods that are no more than a single if - New analysis : suggest to ditch default before assigning it - Updated analysis : Unset or Cast was refactored with less raw() calls - Updated analysis : PPP declaration style - New analysis : collect the number of injections in a constructor - New analysis : collect the property usage level for each class - New analysis : collect structures, instead of in dump - New analysis : collect catch, to complete results with throw collect - Updated analysis : report usage of standalone True, False, Null. - New analysis : report identical cases in match and switch - New analysis : report usage of constants in traits - New analysis : preference between short and formal comparison - New analysis : report yield that can be turned into a yield from - New analysis : report usage of enum cases in static constant expressions - New analysis : report modification of readonly properties in __clone() - New analysis : report usage of internal classes with class_alias() - New analysis : report usage PHP 8.3 new dynamic - New analysis : static variables may be initialized with arbitrary expression in PHP 8.3 - New analysis : report when an interface's class constant visibility is not public when in the class - Updated analysis : upgraded pre-calculate used variable in closure - Updated analysis : Insufficient typehint (extended coverage) - New analysis : Report final trait method that are overwritten - Tokenizer Added support for typed constants - Checked support for readonly anonymous classes - Fixed LINK in DNF types - Added support for attributes in enum, trait, interface and enumcase ## Version 2.5.2 2023-02-04 - Wang Gui - Report New report : Format for SonarCube - Analysis New analysis : report array literal, used by index. - New analysis : Cannot use empty strings with explode() - New analysis : Report max() and min() applied on empty arrays. - Updated analysis : Unused methods now skips internal use - Updated analysis : Date formats are collected only on Datetime and Datetimeimmutable - New analysis : strpos() used to convert integer to their ascii value - New analysis : report double checks in the code - New analysis : skip empty arrays in array_merge() - New analysis : ellipis is slower than array_merge() - Updated analysis : variable type is detected with cast too. - New analysis : follow unvalidated data in $_SESSION - Updated analysis : updated in_array() to also report short arrays - Updated analysis : closure2string skips when other arguments are necessary - Updated analysis : condition is always true is upgraded with more work on is_a() and class type - Updated analysis : htmlspecialchars() changed behavior in 8.1 - Updated analysis : always false does a better job at comparing types - Updated analysis : upgraded analysis with types - New analysis : new functions in PHP 8.3 - New analysis : suggestion for str_ends_with() - New analysis : suggestion for str_starts_with() - Updated analysis : dirname with 3rd arg is suggested when using '$path/../' strings - New analysis : collect the number of arguments per PHP native calls - New analysis : report if/then when a variable is assigned in one branch, but not in the other - New analysis : report mono or multi bytes favorite - New analysis : count the number of arguments to PHP native calls - Updated analysis : Null on boolean now takes into account types - Updated analysis : upgraded Make One Call analysis to spot calls within same expression - Updated analysis : incompatible type with incoming now covers call with superglobals - Updated analysis : fixed bug when calculating DEFINITION for superglobals - New analysis : report different constructors - New analysis : report usage of short ternary operator - New analysis : report when finalizing the call before the closure is better - New analysis : report object cast to int or float - New analysis : report variables initialized before an if condition with reinitialisation - New analysis : report incompatible constructors - New analysis : Report sidelined methods from a trait - New analysis : Report misused Generators - New analysis : Substr() for partitions in a loop - New analysis : suggest caching local calls to reduce processing - New analysis : report list of PHP 8.3 new classes - Tokenizer Added support for readonly + final/abstract class - Fixed DEFINITION for static in new - Fixed DEFINITION for global variable definitions - Upgraded support for variable types with PDFF - Adapted support for undefined Identifier between PHP 7 and 8 ## Version 2.5.1 2023-01-19 - Wang Gui - Architecture Extracted Called* to external class - Introduced parallel loading for nodes and properties (links are WIP) - Analysis New analysis : suggest omitting empty arrays before array_merge() - Updated analysis : more calls are collected - Updated analysis : Strict comparison with boolean covers array_search and array_keys - New analysis : report useless methods - Updated analysis : Add Zero also covers syntax like +$a - New analysis : report weak tests on array, without checks on index - New analysis : report multiple types in switch (PHP 8 compability) - New analysis : could be a readonly class - Updated analysis : Comparison strings to int include in_array() and co - New analysis : report class invasions - New analysis : report property invasions - New analysis : collect all setlocale() calls - Updated analysis : Collected calls includes __construct() - Updated analysis : Collected calls includes __clone() - New analysis : report usage of ++ on strings - New analysis : report usage of deprecated mb_string encodings - Tokenizer Fixed edge cases with readonly/namespace as method name - Fixed handling of static keyword with rare combinaisons ## Version 2.5.0 2023-01-05 - Wang Gui - Architecture - Cobbler - Report - Analysis Refactored analysis : WrongTypeWithCall skips variables without a type - Refactored analysis : BailoutEarly skips blocks with one element only - Refactored analysis : NonStaticMethodsCalledStatic extended to Stubs - New analysis : ambiguous types for variables - Refactored analysis : Unpreprocessed skips static::class - Refactored analysis : Undefined constant skips class constants with variables - New analysis : report exception that can't be chained - Refactored analysis : ShellExec preferences - Refactored analysis : CreateMagicProperty was extended - New analysis : report possible ::class usage - New analysis : report wrong order of argument with variadic - New analysis : report wrong encoding usage with mbstring - Refactored analysis : Sped up 'could be abstract method' - Refactored analysis : Undefined Interfaces differentiate classes and interfaces - New analysis : Ternary and Coalesce Operators order - Refactored analysis : Set Parent DEFINITION also adds DEFINITION for CPM - Refactored analysis : NativeClassTypeCompatibility upgraded fully to stub support - New analysis : Report useless assignation of promoted properties - Refactored analysis : Parameter name checking works with methods - Refactored analysis : Classes/CouldUseClassOperator is extended to all CITE - Refactored analysis : Classes/UndefinedConstants skips situations where the class is a variable of unknown type - Refactored analysis : Infinite recursion also detects coalesce - New analysis : Report methods / property confusions - New analysis : Suggest using __NAMESPACE__, instead of hardcoded string - Refactored analysis : Indirect injection is extended with ?? ?: and ? : - New analysis : Report too many chained calls one in the other - Refactored analysis : 'This is for classes' is extended to traits and enums - Refactored analysis : 'Unsupported types with operator' is now using Stubs files - New analysis : Report wrong typed with incoming values - Refactored analysis : 'Queries in loops' is now using extended to methods and one functioncall down. - Refactored analysis : Identical Variables in Foreach now searches inside the source - New analysis : Empty Loops - New analysis : Report arrays that are too much extracted - New analysis : Report methods where variables are not needed (only unique usage) - New analysis : Report possible emission of TypeError - Refactored analysis : Cant Throw now skips Interfaces - Refactored analysis : fixed false positive with Always False - Refactored analysis : Constant Invalid names do not confuse the constant and its value - Refactored analysis : Undefined Variable in Catch, now skips variables also created in the catch clause - Refactored analysis : Implicit conversion to int : skip float returned values - Refactored analysis : Closure could be static now checks for internal definitions of enums or anonymous class - Refactored analysis : Dont Collect void is extended to unspecified return types - Refactored analysis : useless coalesce - Refactored analysis : Indirect Injections - Refactored analysis : Useless Reference now checks PHP, ext and stubs - New analysis : Suggest to throw exceptions with json_*code() - Refactored analysis : Scalar are not arrays cleaned - Refactored analysis : No net for xml now enforces class too - Refactored analysis : Static for classes now omits static variables - Refactored analysis : Incompatibility signature now omits __construct - Refactored analysis : Unreachable code - New analysis : collect all calls from methods to methods - New analysis : set fullnspath to method calls - New analysis : report variables with an initial capital S (readability) - New analysis : type dodging in parameter with union type - Tokenizer Fixed bug with related to readonly position - Fixed bug where define was not correctly set with fullnspath - Fixed priorities for print and yield - Added support for DNF in the engine - Added definition with static calls, within a class - Added support for methods and properties with static calls to parent:: - Refactored handling of scope with $this and self/static - Created a Precedence class for each version - Refactored calculations for currentMethods in external class - Migrating from Method to readsStubs (WIP) - Handled edge cases in Yield (yield yield) - Removed link between bool and int values when loading (edge case of numeric strings) - Cleaned Load of GlobalVars array ## Version 2.4.9 2022-09-07 - Wang Gui - Analysis Refactored analysis : Uses Default now supports PDFF and functions - Refactored analysis : Using PDFF with ext/seaslog and ext/memcache - Removed analysis : ext/wikidiff2, ext/wincache, ext/iis, ext/libevent, ext/mhash, ext/parsekit, ext/kdm5 - New analysis : date() versus DatetTime preferences. - New analysis : identify unused public methods - Refactored analysis : Detecting wrong visibility with implemented methods was sped up - Removed analysis : Interface/ConcreteVisibility, double with Classes/ImplementedMethodsArePublic - New analysis : identify potential abstract methods - Refactored analysis : Upgraded 'Wrong Type With Call' to use the known variable types - Refactored analysis : No Parent now takes traits into account. - Refactored analysis : Should Have Destructor : removed some false positives, refactored documentation. - Refactored analysis : No Parent now also checks for traits - Refactored analysis : Uses default argument skips Virtualproperties - New analysis : Complete/SolveTraitConstants adds support for constants in traits (PHP 8.2) - Refactored analysis : Complete/SetParentDefinition was trimmed of 2 useless queries - Refactored analysis : PPP declaration style - Refactored analysis : Is Global Constant (removed usage of .ini) - Refactored analysis : Overwritten* are simplified for speed up and deduplication - Refactored analysis : UndefinedClasses speed up - Refactored analysis : Should Preprocess now adds Heredocs and skips variables inside strings - Refactored analysis : Should use Ternary now skips elsif - Refactored analysis : ext/fann now use pdff - Tokenizer Added support for PHP keywords in namespace names. ## Version 2.4.8 2022-08-24 - Xue Rengui - Architecture - Cobbler - Report - Analysis Refactored analysis : strange names now covers types too. - Removed analysis : ext/proctitle, Composer/IsComposerName, ext/cyrus - Removed analysis : Composer/IsComposerInterface, - Refactored analysis : VariableTypehint now skips self-transforming variables in default - Refactored analysis : ErrorMessages now also tracks trigger_error() - New analysis : ext/teds, ext/scrypt, ext/geospatial - Refactored analysis with pdff : ext/crypto, ext/ev, ext/enchant - Refactored analysis : refactored 'could use short assignation' - Removed analysis : ext/ereg, ext/async - Refactored analysis : undefined class constants are also looked in the children classes - Refactored analysis : vendor/symfony and vendor/phalcon - Refactored analysis : Unused Methods now handles foreach() with new() - New analysis : vendor/feast framework - Checked unit tests : 4480 / 4450 test pass (99.3% pass) - Tokenizer Fixed detection of constant in ternary/coalesce - Finish adding types ## Version 2.4.7 2022-08-03 - Xu Jingzong - Architecture - Cobbler New cobbler : remove brackets to single-instruction commands - Report New inventory : IP - Analysis Refactored analysis : Could Use Array_sum() - Refactored analysis : Wrong Attribute with properties - Refactored analysis : implode Args order now support types - Refactored analysis : fopen mode does accept rw - Refactored analysis : references on objects (full refactor) - New analysis : finding empty arrays with comparisons - New analysis : using strict with in_array or not - New analysis : no default for referenced parameter - New analysis : No clone constant before PHP 8.1 - New analysis : Complete enum cases with definition to value and name - Refactored analysis : better handling of clone in Variable Typehint - Refactored analysis : cleaned some false positives with Undefined Properties - Refactored analysis : Unresolved use now uses stubs; upgrade in function/const coverage - Removed analysis : ext/recode, ext/runkit, ext/ming - Refactored analysis : Better coverage for 1 + [] - Refactored analysis : Difference preference has gremlin upgraded - New analysis : Ext/random (PHP 8.2) - New analysis : IP inventory - Refactored analysis : JsonSerialize and ReturnTypeWIllChange cover new methods - Tokenizer Added support for -> out of Enum cases (with name and value) - Added new classes from PHP 8.2 - Fixed missing fullnspath for attributes with absolute path - Added all attributes to properties ## Version 2.4.6 2022-07-20 - Li Yuanji - Architecture Skip loading of WS property when only doing an audit (speed up loading) - Finished moved to Gremlin 3.6 - Cobbler New cobbler : adds brackets to single-instruction commands - Report Ambassador : refactored trait matrix - Analysis Refactored analysis : Wrong Type Hint with First Class Callable - New analysis : PHP 8.2 new functions - Refactored analysis : Useless Cast takes advantages of const types - Tokenizer Typed all internal atoms - Added types to internal loading engine ## Version 2.4.5 2022-07-07 - Li Yuanji - Architecture Docs : fixed presentation for cobblers - Cobbler New cobbler : remove abstract option - Report - Analysis Refactored analysis : No Pss Outside Class also checks for static closures - New analysis : Report errors in sprintf() formats - New analysis : Report methods and properties with the same name in a class - New analysis : Report invalid chars in date scanning formats - Refactored analysis : Useless Coalesce applied to PHP native methods - New analysis : Report Abstract Private methods in traits (php 8.0-) - Refactored analysis : Dynamic New now also works on parenthesis - New analysis : Report Utf8_encode() and utf8_decode() deprecation - Refactored analysis : Create Default Values checks on self-transforming variables - Refactored analysis : Missing Typehint skips constructor and destructor - Refactored analysis : Useless constructor skip one that has other constructor calling it - New analysis : Some Magic methods have compulsory return types - Refactored analysis : Overwritten const is extended to classes without constants (but in their parent or interfaces) - Refactored analysis : Nested ternaries now checks assignations, New parameter to set the min depth - Refactored analysis : Instantiating Abstract now uses PDFF - Refactored analysis : $this may be OK in closures (they can be rebinded later) - Refactored analysis : Adding 'Void' returntype when possible - Refactored analysis : Don't Collect Void was upgraded with methods returning nothing. - Refactored analysis : Identical Expressions, now checks = and omits short assignations - New analysis : If Then Return Favorite - Refactored analysis : Useless Casting checks % distinctly - Refactored analysis : Add Zero skips variables more often - New analysis : Could Be Resource - New analysis : DateTime Immutable is not immutable - Tokenizer Fixed namespace's names dectection for older PHP versions - Fixed Functioncall detection inside a new operator. ## Version 2.4.4 2022-06-23 - Li Jiancheng - Architecture Upgraded to Gremlin 3.6.0 (tinkergraph) - Prepared engine to work with GSneo4j 3.6.0 - Cobbler New cobbler : turn ${a} into {$a} for PHP 8.2 compatibility - Refactored cobbler : Adds null type to nullable parameters - Report - Analysis Refactored analysis : Non nullable setter skip properties set in constructor - Removed analysis : ext/ffmpeg, ext/fdf, ext/xcache, ext/yis, ext/cairo - Refactored analysis : ext/rdkafka, ext/zookeeper now uses PDFF - Refactored analysis : Should Preprocess, now include local constant strings - Refactored analysis : Undefined Interface, now not reporting extra Types - New analysis : retyped reference, when a parameter with a type, eventually get a new type - Refactored analysis : Static methods called from object, modernization - Refactored analysis : New Analyzers, omits local defaults values - Refactored analysis : Access Protected now takes into account PDFF - Refactored analysis : Null type detection includes null defaut value for parameters. - New analysis : Report type error for default values - Refactored analysis : 'ds', 'ssh2' were upgraded to PDFF - Checked unit tests : 4373 / 4349 test pass (99.5% pass) - New analysis : Ice framework - New analysis : taint - Tokenizer Fixed 'constant' bug with functioncall on a nsname - Upgraded Typehint detection to handle clone() calls - Upgraded Typehint inference for properties and variables ## Version 2.4.3 2022-06-02 - Emperor Gaozu of Tang - Architecture Doctor failed to copy the tinkergraph configuration files - Removed old connector GSneo4j/Tinkergraph - Refactored starting/emptying of gremlin database - Testing on PHP 8.2 - Cobbler Added suggestions when the -P is not found - New cobbler : add Final to classes - New cobbler : removes Final from classes - Upgraded cobbler : removes Readonly from classes - Report Ambassador, Emissary, Diplomat : removed link to the source code. - Ambassador, Emissary, Diplomat : fixed link to online documentation - Analysis Fixed analysis : Undefined Classes and Trait where affected by the recent Complete/Returntyping - Refactored analysis : 'Variables Used Once' not omit inherited parameters. - Refactored analysis : 'Functions without return' not skip methods with Never and methods that throw in the main sequence. - New analysis : 'Parent is not Static', but rather self - Refactored analysis : 'Use This' - Refactored analysis : 'Extension/Extxhprof' to PDFF - Refactored analysis : Removing usage of methods, moving to PDFF - New analysis : 'No magic method for Enums' - Refactored analysis : 'Multiple Identical Keys' now also processes automated index - New analysis : 'Modifying Readonly' (WIP) - Refactored analysis : 'Could use short assignation' skips usage of ?? - New analysis : 'Readonly Can only be assigned in defining class' - Refactored analysis : 'Runkit7' was upgraded to PDFF - Refactored analysis : 'Gnupg' was upgraded to PDFF - Refactored analysis : 'xdiff' was upgraded to PDFF - Refactored analysis : 'event' was upgraded to PDFF - New analysis : ext/stomp, ext/csv - New analysis : Suggestion making the default assignation in property definition - Refactored analysis : 'Redefined private properties' now covers PDFF too - Refactored analysis : 'Failing Stubstr Comparison' now accepts != - Refactored analysis : 'Insufficient typehint' extended with class constants - Refactored analysis : 'Unused constant' takes advantage of hierarchy - Refactored analysis : 'Useless Abstract' extended to include single extended classes - Refactored analysis : 'Mismatched Default Value' now omits parameters without default value - New analysis : method is identity - New analysis : report overloaded existing names in use, from PDFF - New analysis : collect incoming date inventory - New analysis : collect vendor's API usage - New analysis : report Array addition usage - Checked unit tests : 4373 / 4349 test pass (99.5% pass) - Tokenizer Added support for PHP 8.2 readonly classes - Fixed bug that made VariableTypehint automatically isPHP ## Version 2.4.2 2022-05-18 - Li Chunfeng - Analysis Refactored analysis : 'Raised access Level' now supports PDFF files - Refactored analysis : 'Cant Extends Final' also Works with anonymous classes - New analysis : Report 'Lowered access levels' - Refactored analysis : 'Final methods' extended to traits - Refactored analysis : 'Overwritten Methods' fixed bug with Traits - New analysis : 'Cant extends Final Methods' - Refactored analysis : 'Cant extends Final Constants' with PDFF support - New analysis : 'Extension Excimer' - New analysis : 'Report implicit float to int conversions' - Refactored analysis : 'Is always false' is extended to typed properties - New analysis : 'Report inegalities with different types' - New analysis : Report traits used once - Refactored analysis : 'Is Not Implements' now supports PDFF; support for trait added. - Refactored analysis : 'Wrong name with paramter' : added support for PDFF - Fixed analysis : 'Overwritten Methods' skipped some interfaces - Refactored analysis : 'Fossilized methods' was counting methods that are defined with Virtualmethod - Refactored analysis : 'Fix bug' when missing fqn in New for Classes/WrongTypedPropertyInit - New analysis : Report unknown locales. - New analysis : ext/pkcs11 - New analysis : ext/spx - Checked unit tests : 4314 / 4317 test pass (99% pass) - Refactored analysis : 'Basename suffix' detection extended - Tokenizer Fixed bug with float and power - Fixed bug in global variable creation - Create all possible links to static keyword - Speed up creation of links to $GLOBALS ## Version 2.4.1 2022-05-04 - Yuan Tiangang - Architecture New Dump : collect all stub's structures - Report Sarif : Fixed URI (no initial /) and Exakat version - Unused : report unused stuff in the code - Ambassador : upgrade presentation of the Exception Treephp - Analysis New analysis : Deprecated String interpolation in PHP 8.2 - Refactored analysis : Spaceship features is used for isRead property - Refactored analysis : Skip analysis of returntypes for methods with throw/assert/trigger_error() - New analysis : Report unused Enumeration Cases - Refactored analysis : Can't instantiate class now takes local class into account - Refactored analysis : Many new examples extracted from the docs - Refactored analysis : fixed bug with 'Wrong Type With Call' - Refactored analysis : Conditional structures now includes Enums too. - New analysis : Don't throw raw exceptions - New analysis : Useless Coalesce operator (when there is a type available) - New analysis : ext/yar - Refactored analysis : 'Wrong number of argument' now includes methods defined in a trait in a PDFF - Refactored analysis : moved ext/amqp to PDFF ## Version 2.4.0 2022-04-20 - Yin Kaishan - Report Ambassador : suggest literals to be turned into a constant, based on assignation and comparison - Analysis Refactored analysis : 'Classes/WrongCase' reported too many arguments - New analysis : No constructor in interfaces - Refactored analysis : Bail Out Early also report if/then when in last position of an sequence - Refactored analysis : Useless Casting also checks for double application of typehint/cast - New analysis : Could Be A constant (in Dump) - New analysis : Could Be Spaceship - Refactored analysis : Vendors/Concrete5 is updated to Concrete5 v9.0 - New analysis : Vendors Sylius - Refactored analysis : Vendors/Joomla is updated to Joomla 4.2.0 - Refactored analysis : Wrong Number Of Arguments supports Constructors and methods (static and normal) ## Version 2.3.9 2022-04-06 - Fu Yi - Architecture Changed Loading system to handle globals directly with gremlin, and without ids - Cobbler New cobbler : adds 'function array_key_exists' to the list of use statements to speed up array_key_exists. - Analysis Refactored analysis : Fixed bug with 'each' and namespaces in Php/Deprecated - Refactored analysis : Next Month Trap was updated with support for datetime (Immutable) - Refactored analysis : TimeStamp Differences now covers any seconds additions. Datetime::format('U') was also added to sources. - New analysis : Avoid using 86400 to handle days when calculating dates. - New analysis : Do not reuse the source name in a foreach($a as $a) - New analysis : Use constants when the function returns them - Updated analysis : New constants for 'Use Constants As Arguments' - Refactored analysis : many Extensions/Ext* are moving to pdff support - Refactored analysis : speedup Should Preprocess analysis - Refactored analysis : Modernized Overwritten class constants - New analysis : Report overwritten final constants from PDFF - Refactored analysis : Moving Extensions/Ext* to PDFF - Refactored analysis : Repeated Regex - New analysis : Report string / integer comparison for PHP 8.0 migration - Refactored analysis : Defined Class Constants differentiate from Enumeration cases - New analysis : Complete functions with obvious typehints - New analysis : Extension protobuf - Refactored analysis : Upgraded Property analysis to use PDFF - Refactored analysis : 'Multiple identical keys' now has an array size limit (15000) - New analysis : Constant favorite : use or not? - Refactored analysis : Upgraded 'Unresolved classes' with Pdff support - Tokenizer Fixed isPhp/isExt/isStub detection for catch classes ## Version 2.3.8 2022-03-23 - Xiao Yu - Architecture Speed up gremlin queries - Report Pdff : added support for hasDefault in properties and parameters - Analysis New analysis : Report type of string introspection used in the code, as a favorite - New analysis : Report functions to be of type 'never'. - Refactored analysis : Variables used once by context, now omits Blind variables - Refactored analysis : Redeclared PHP functions works with PHP 8.1's functions - Refactored analysis : Modern Empty - Refactored analysis : Deprecated Functions - Refactored analysis : Removed usage of IsExtInterface in UndefinedClasses - Refactored analysis : Suggesting static class names over objects takes into account the nature of the typehint available. - Refactored analysis : Using PDFF with ext/gender, ext/decimal, ext/xxtea, ext/mailparse, ext/uuid. - Refactored analysis : Using PDFF with ext/xmlreader, ext/writer, ext/mongodb, ext/gd, ext/dom - Refactored analysis : Class Usage rule now skips Interfaces in Implements - Removed analysis : Modules/* - Removed analysis : Extensions/Extzbarcode ## Version 2.3.7 2022-03-09 - Xiao Yu - Architecture Fixed all internal step's case - Report New report : PerRule (same as PerFile, but grouped by rules) - New report : CompatibilityPHP56 (based on Perfile, dedicated to Compatibility PHP 5.6) - Updated report : Ambassador now lists @keywords in phpdocs (inventories) - Updated report : Manual includes sections for namespaces, and global constants - Analysis New analysis : Use variables when they are created inside a loop - New analysis : Simplify Foreach() - New analysis : Identical Conditions on If-elseif - Refactored analysis : Undefined Instanceof now relies on isPhp/isExt/IsStub - Refactored analysis : First byte only, now uses variable typehints - Refactored analysis : Dont loop on yield - Refactored analysis : Interfaces suggestion now accepts php/ext/stubs configuration - Refactored analysis : Static calls to traits exclude self, parent, static - Refactored analysis : Don't read and write at the same time : Extended to all containers, removed edge cases - Refactored analysis : Undefined interfaces takes Variable Typehint into account - Refactored analysis : Incompatible Method signature - Refactored analysis : Unfinished objects now checks called internal methods - Refactored analysis : Better coverage for Class Constants - Refactored analysis : Insufficient typehint skips properties without a type - Tokenizer Extended support for Variable typehints ## Version 2.3.6 2022-02-16 - Qin Qiong - Architecture - Cobbler Refactored cobbler : 'SetTypehint' checks more before adding a class typehint - Report Ambassador : added the list of extended dependencies as an audit report - Diplomat : removed 4 rules from Analyze (Classes/Redefined*) - Analysis New analysis : Too Many Stringed If-then-elsif - New analysis : Undefined Enumeration case - New analysis : Unfinished objects - New analysis : Class Alias usage - New analysis : Undefined Methods - New analysis : Suggest array_sum(), from the code - New analysis : Missing type on any structure (method, parameter, property) - New analysis : Spot unreachable methods - New analysis : Public Reach lists the paths from public methods to private ones. - New analysis : Avoid Static calls on objects when possible - Deprecated analysis : Is Php Function - Refactored analysis : Removed usage of IsExtFunction analysis - Refactored analysis : 'Could Be array' relies on ... too - Refactored analysis : 'No need for else' now skips elseif - Refactored analysis : 'Undefined constants, functions, traits, interfaces, classes{const, static P/M}' now leverages the stubs - Refactored analysis : 'Insufficient typehint' checks for union types - Refactored analysis : 'Used Once Properties' now omits classes that have dynamic properties - Refactored analysis : 'Unused class constants' - Refactored analysis : 'Reuse variable' has a narrower focus, and takes scope into account. - Refactored analysis : 'Weak Type' Extended analysis to typed containers - Refactored analysis : Definitions stats now break down to isPHP/isStub/isExt - Refactored analysis : Isset() calls with more complex expressions - Bug: fixed PHp/MixedKeyword in analyzer database - Checked unit tests : 4123 / 4132 test pass (99% pass) - Tokenizer Refactored Foreach variable detection - Fixed constant detection in deep namespaces - Restored Stubs from configuration and commandline - Added fullnspath to static properties - Added Complete/Is*Structure, to finish marking atoms with isPhp, isStub - Deprecating Composer/IsComposerNsname - Fixed bug with class_alias - Added Not to guess list - Fixed bug in engine with comments at the end of scripts. ## Version 2.3.5 2022-02-02 - Yuchi Gong - Architecture 'Complete' ruleset will run the configured rulesets that are not already run - Cobbler New cobbler : removes readonly option on properties - New cobbler : removes useless variables - Report Ambassador : added counts with the actual sizes of the classes (constants, properties, methods) - Ambassador : Fixed display of compatibility features - Uml : Report number of classes exported - Analysis New analysis : List all external dependencies extensions - New analysis : report recycling of foreach() sources - New analysis : report usage of readonly - New analysis : Suggest updating if-then to ternary operator - New analysis : Report multiple similar calls in a row - New analysis : Suggest using FILE_APPEND with file_put_contents() - New analysis : Report missing visibilities - New analysis : Identify literal that may actually be existing constants. - Fixed analysis : Cancelled parameter shall take ??= into consideration - Refactored analysis : 'Cannot use static with closure' analysis is extended to properties - Refactored analysis : Upgraded detection of variable modified by a reference in a PHP or custom function/methodcall. - Refactored analysis : Fixed bug with 'This is for class' where typehint where not correctly seen inside a class. - Refactored analysis : 'Insufficient typehint' was upgraded with class constants checks - Refactored analysis : 'Undefined class' skips ? as a class - Refactored analysis : 'Static loops' now takes into account modifications in the conditions - Refactored analysis : 'Complex expressions' omits match - Refactored analysis : 'Cache variable outside loop' fixed bug with function names and new expressions - Refactored analysis : 'Logical mistakes' now checks for constants on the rest of the comparison - Refactored analysis : 'Cant instantiate class' now takes into account self/static - Refactored analysis : 'Should use self' also reports self opportunities in new expression. - Refactored analysis : 'Written only' fixed a bug with propperties - Refactored analysis : 'No choice' also spots ?: null and ?? null - Refactored analysis : Written Only Variable now takes into account references in parameters - Refactored analysis : Classes's strange names covers methods, properties and classes. - Refactored analysis : Caught but never thrown exceptions have an updated list of exception - Refactored analysis : Unresolved Catch uses updated PHP exception/error list - Refactored analysis : PHP 8.0 new types now covers mixed and also properties. - Refactored analysis : PHP 8.0 union type differentiate between ?A and null|A - Refactored analysis : CIT same names was extended to Enumeration - Tokenizer Fixed boolval for multiplications - Fixed spaceship for string and boolean values - Added processing to isPhp/isExt/isStub to implemented names ## Version 2.3.4 2022-01-19 - Yuchi Gong - Cobbler New cobbler : remove unused use expression - Added 4 directives to each rules : namespaces, ignore_dirs, include_dirs and file_extensions. They filter out some of the results. - Report Composer : upgrade the list of core PHP extensions - Analysis New analysis : Mark simple getters/setters in classes - New analysis : Report unchecked divisions (int and operators) - New analysis : report possible abstract constants in classes (which should be defined in a parent) - New analysis : report recycled variables - Refactored analysis : Upgraded 'Object references' with union and intersectional types - Refactored analysis : Removed edges cases in 'Don't collect void' - Refactored analysis : Extension detection now takes into account enums - Refactored analysis : Upgraded AlwaysFalse with better typehinting inference - Refactored analysis : indentation levels missed several results while reporting - Refactored analysis : interfaces, traits and constants were missing for use expression resolution - Refactored analysis : Undefined Interfaces now exclude better PHP or ext's interfaces - Refactored analysis : Never Used Parameter confused Void and first argument - Refactored analysis : Self were reported as outside a class when in foreach() - Refactored analysis : Clone with non-arrays now checks PHP native functions too - Refactored analysis : Excluded powers from calculations in IsZero - Refactored analysis : Fixed discrepancy between ' and " handling of \ - Extended tests : match without default - Tokenizer Fixed a bug where static keyword is processed as a simple nsname - Fixed a bug where typehints were not marked as isPhp, isExt or isStub - Fixed an edge case with array functions inside match() syntax - Fixed an edge case with Closures and reference-use variable - Fixed an edge case with static inside ternary - Fixed yield expression scope - Added Table for PHP 8.2 compilations checks - Removed extra void with use expression for traits ## Version 2.3.3 2022-01-05 - Xu Maogong - Cobbler New Cobbler : removes attributes - Report - Analysis New analysis : suggest using ?-> when Null is a possiblity - New analysis : Report backward incompatibility with overloaded interface constants - New analysis : Mark variables as local constants when only assigned once - New analysis : suggest using iterable, based on array|traversable usage - New analysis : Report usage of PHP 8.1 intersection typehints - Refactored analysis : Hidden Nullable rule now handles intersection types - Refactored analysis : 'Use Nullable' covers properties too - Refactored analysis : 'Could Be stringable' is extended to trait usage - Refactored analysis : skip static and globals when counting variable usage in methods - Refactored analysis : PHP 8.0 Union type detection includes properties - Added tests to Complete/Overloaded* (CPM) - Tokenizer Fixed a bug with Ternary and constants ## Version 2.3.2 2021-12-16 - Wei Zheng - Cobbler New cobbler : removes a method - Report - Analysis New analysis : suggest ::class instead of get_class() - New analysis : report when a class extends stdclass (for dynamic properties review) - New analysis : Reports when checks are made on the existence of properties - Upgraded analysis : Useless Typechecks is upgraded with union and intersectional type checks - Upgraded analysis : Reporting invalid access to protected CPM - Upgraded analysis : Removed Used Properties with classes with dynamic properties - Fixed bug in PropagateConstants - Tokenizer Added detection of typehints for variables ## Version 2.3.1 2021-12-01 - Li Shimin - Cobbler Fixed bug with Settypehint when multiple types are available - Report New Pdff report : PHP Document File Format - Analysis New analysis : report promoted properties - New analysis : report deprecated PHP 8.2 callable - New analysis : report new in initializers - New analysis : report nested attributes - New analysis : report direct calls to Trait methods and properties - New analysis : report auto vivification of false (PHP 8.1) - New analysis : report implicit float to integer conversion for arrays - Updated analysis : Declare Static and Global early. - Updated analysis : No Null For Native now uses typehints - Updated analysis : refined No Static variable in method - Tokenizer Fixed bug with __METHOD__ when it is called outside a method ## Version 2.3.0 2021-11-18 - Wei - Architecture Catchup tokens from PHP 5.6 till 7.2 - Report unknown Rulesets during reports command - Extended 'catalog' command to list rules too - Extended 'catalog' command to return YAML format - Report Added several new analysis to the Rector report - Added mixed and never to Appinfo report - Ugraded Sarif report with bartlett/sarif-php-sdk - Analysis New analysis : report the missing mixed returntype for jsonserialize - New analysis : report final with constants - New analysis : report never usage (typehint) - New analysis : report PHP 8.1 typehint incompatibilities - New analysis : report PHP 8.0 typehint incompatibilities - New analysis : report PHP 8.0 named parameters - New analysis : report First Class Callable Syntax - New analysis : New Functions in PHP 8.1 - New analysis : Removed functions in PHP 8.1 - New analysis : Prepare 'never' for PHP 8.1 - New analysis : Prepare 'mixed' for PHP 8.0 - New analysis : detect mixed and never usage as typehints - Upgraded analysis : Wrong Number of arguments also works with new first class callable syntax - Upgraded analysis : Typehint stats now includes union and intersection types - Upgraded analysis : Removed functions in PHP 8.0 ## Version 2.2.5 2021-11-03 - Wood star - Analysis New analysis : Calling Trait Static Method directly is deprecated in PHP 8.1 - New analysis : No reference for returned void - New analysis : No Null for PHP native methods - Updated analysis : Wrong type for argument now covers classes, union type and intersection types. - Updated analysis : Wrong type for argument now covers classes, union type and intersection types. - Updated analysis : Unused Private Methods are also detected with array($this, 'xx') syntax - Checked unit tests : 3821 / 3805 test pass (99% pass) - Cobblers New cobbler : remove typehints from arguments, returns and properties ## Version 2.2.4 2021-10-21 - Gold star - Dataset Updated PHP native dataset with missing classes and typehint. - Analysis New analysis : Report incompatible typehint with native PHP methods in PHP 8.1 - New analysis : Report Missing Attribute Attribute - New analysis : Report full_path index in $_FILES usage - Updated analysis : Type detection also include return type from methods - Cobblers Updated cobbler : Set typehint handles typehint from arguments - Tokenizer Added more cases for Constant types ## Version 2.2.3 2021-10-06 - Wu - Architecture Updated INI files for PHP 8.1 - Data Extended PHP directives lists - Report New report Migration 8.1 - Analysis New analysis : PHP 8.1 removed directives - New analysis : PHP 8.1 removed constants - New analysis : Wrong named parameter for PHP native function - New analysis : Report duplicate named arguments - New analysis : htmlentities (and co) default 2nd argument - Updated analysis : Scalars are not arrays. Extemded with type support. - Tokenizer Support for callable strlen(...) - Test for new syntax for octal 0o123 ## Version 2.2.2 2021-09-22 - Si - Architecture Refactored documentation - Report Added support for PHP 8.1 compatiblity - Analysis New analysis : Restrict $GLOBALS usage - New analysis : No object as array's index - New analysis : Overreaching classes (PHP feature) - New analysis : Report Enum usage - Updated analysis : Typehints/* got new Unit Tests - Updated analysis : Explode optimisation - Tokenizer Reduced the number of DEFAULT creation for properties - Added support for new PHP 8.1 syntax (Enum ) ## Version 2.2.1 2020-11-20 - Chen - Architecture Export : WIP of exporting PHP code from graph - New directives : rules_version_max, rules_version_min, ignore_rules and ignore_namespace - Report Sarif : Fixed line number that may be null or less - Ambassador : Fixed visibility report - Analysis New analysis : check for match as a keyword - New analysis : replace static variable by static properties - New analysis : warn about usage of get_object_vars() - New analysis : report global and static variables that are declared multiple times - Updated analysis : extended Used Classes to abstract classes - Updated analysis : wrong number of argument now supports $this() - Updated analysis : parse_str last argument doesn't apply anymore in PHP 8 - Updated analysis : useless argument now omits parameter with default value - Checked unit tests : 3797 / 3800 test pass (99% pass) - Tokenizer Fixed race condition with phpdocs - Refactored static and global variables definitions (avoid double definitions) - Fixed detection of [] inside a list() - Fixed detection of alternative syntax for switch - Added use property to usenamespace too (for grouping) ## Version 2.2.0 2020-10-15 - Mao - Architecture Extended Export command to produce PHP scripts from the graph database - Added more typehints - Added new command 'onefile' - Sped up database restart with id reset - Updated list of functions for several extensions. Started adding methods, class constants.. - Report Ambassador : updated popularities - Ambassador : added missing PHP 8.0 ruleset - Analysis New analysis : report arguments and properties whose name clashes with the typehint - New analysis : report long preparation before throw command - New analysis : missing __isset() method - New analysis : suggest array_keys() for array_search in loops - New analysis : array_map() complains with values by reference - New analysis : report final private properties - New analysis : report misnamed constant/variable - New analysis : check for attribute configuration (PHP 8.0) - New analysis : suggest dropping variable in catch clause - New analysis : report resources that should not be tested with is_resource (PHP 8.0) - New analysis : check for named arguments and variadic - Updated analysis : wrong number of argument now supports $this() - Updated analysis : redefined private property uses OVERWRITE - Updated analysis : refactored UndefinedFunctions for speed - Updated analysis : array_map() complains with values by reference - Updated analysis : removed false positives on properties in strings - Updated analysis : unsupported types with operators skips cast values - Updated analysis : cancelled parameters are also for array_map/array_walk - Updated analysis : variable variable skips variables inside strings - Updated analysis : removed functions are not reported when in if/then with function_exists() - Updated analysis : wrong optional parameter fixed false positive with ... - Updated analysis : extended list of removed directives, functions and constants - Removed analysis : RealVariables - Checked unit tests : 3761 / 3772 test pass (99% pass) - Tokenizer Added Void to empty default/case - Bitoperation added to isRead - Fixed list[] in a Foreach - Fixed token T_OPEN_DOLLAR_CURLY_BRACKET ## Version 2.1.9 2020-10-01 - Yin - Architecture Removed old and unused commands - Modernized usage of docker as phpexec - New directive php_extensions to managed list of ext - Report Ambassador : removed 3 gremlins from typehint stats, added scalar types - New Migration80 report, dedicated to PHP 8.0 migrations - New Stubs.ini report, dedicated to exakat extensions production - Analysis New analysis : report arguments which are not nullable because of constants. - New analysis : could use stringable interface - New analysis : suggest explode()'s third argument when applicable - New analysis : suggest PHP 8.0 promoted properties - New analysis : report arrays with negative index, and auto-indexing - New analysis : report unsupported types with operators - New analysis : report usage of track_errors directive (PHP 8.0) - New analysis : report useless types on __get/__set - New analysis : count the number of use expressions in a file - New analysis : Avoid modifying typed arguments - New analysis : Report Assumptions in the code - New analysis : array_fill() usage with objects - New analysis : mismatch between parameter name and type - Updated analysis : magic methods definitions also find usage for __invoke() - Updated analysis : noscream operator usage may have exceptions - Updated analysis : identical methods and identical closures - Updated data : list of exceptions and their emitters - Tokenizer Upgraded detection of extensions' structures, beyond functions ## Version 2.1.8 2020-09-18 - Chou - Architecture added '--' options, and kept the '-' options, for migration purposes. (--format and -format are both available) - Added support for PHP 8 attributes in dump.sqlite - Added 'precision' to rule docs. - Moved all but one data collection from Dump -collect to Dump/ analysis. - Report New report : SARIF - Typehint suggestion report : Tick classes when they are fully covered - Weekly report : fix donuts display. - Stubsjson : Added support for PHP attributes - Stubs : Added support for PHP attributes - Analysis New ruleset : CI-Checks - New analysis : 'Multiple declare(strict_types = 1)' - New analysis : 'No more (unset) in PHP 8' - New analysis : Cancel methods in parent : when methods should not have been abstracted in parent class. - New analysis : '$php_errormsg is removed in PHP 8' - New analysis : 'Mismatch Parameter Name' checks parameter names between inherited methods for consistency - Upgraded analysis : 'Useless Arguments' is accelerated - Upgraded analysis : 'Don't use Void' weeded out false positives - Upgraded analysis : 'Wrong type for native calls' weeded out false positives - Upgraded analysis : 'Non static methods called statically' was refactored for PHP 8.0 support - Upgraded analysis : 'PHP Keywords' includes 'match' - Upgraded analysis : 'Useless instruction' reports '$a ?? null' as useless. - Upgraded analysis : 'Uncaught exceptions' is extended to local variables - Upgraded analysis : 'Foreach favorites' also covers the keys - Upgraded analysis : 'Should Preprocess' skips expressions with constants - Upgraded analysis : 'Compare Hashes' has more functions covered - Removed analysis : 'Normal Properties' : no need anymore. - Tokenizer Moved isPhp attribute to Task/Load plugin - Created isExt attribute to Task/Load plugin ## Version 2.1.7 2020-09-07 - zi - Architecture Refactored loading class, to keep query load at optimal size for Gremlin - GC during load to free memory - More typehints - Move several collections to Dump/ ruleset - Report Upgraded Typesuggestion report with report on closures and arrow functions - Added Arrowfunctions in inventories - Added collection of arguments and details for closures and arrowfunctions - Analysis New analysis : Could Be In Parent : suggest methods that should be defined in a parent - New analysis : Don't pollute namespace - New analysis : report insufficient return typehints - Upgraded analysis : 'Method signature must be compatible' now PHP 8.0 compatible - Upgraded analysis : 'Wrong type with native function' fixes false positives - Upgraded analysis : 'Same condition' added coverage for || conditions - Upgraded analysis : 'Missing returntype' extended to class typehints - Upgraded analysis : 'Should Use This' also covers special functions like get_class_called() - Upgraded analysis : 'No concat in loop' skips nested loops - Upgraded analysis : 'Always false' covers typehint usage - Upgraded analysis : 'NoChoice' doesn't report large expressions - Upgraded analysis : 'Dont mix PlusPlus' skip () and = - Upgraded analysis : 'Fallthrough' don't report final cases without break - Checked unit tests : 3663 / 3630 test pass (99% pass) - Tokenizer Removed 'root' property - Upgraded to new Attributes #[] in detection and normalisation - Fixed constant detection within instanceof - Created RETURN and RETURNED for Arrowfunctions (there is no return otherwise) - Parent method also calls children methods when those are not defined there - Support for multiple attributes in one syntax ## Version 2.1.6 2020-08-28 - Night Patrol Deity - Architecture More typehints coverage - Various speed-up - Lighter logging with gremlin - Fixed installation path - Report Upgraded Typesuggestion report - Upgraded Stubs and Stubsjson - Analysis New analysis : report PHP 8.0 unknown parameters - New analysis : overwritten methods with different argument counts - New analysis : Warn of iconv and TRANSLIT for portability - New analysis : Warn of glob and {} for portability - Upgraded analysis : 'Useless check' covers new situations. - Upgraded analysis : 'Abstract away' now covers new calls. - Upgraded analysis : 'Must return Typehint' skips Void. - Upgraded analysis : 'Missing new' with less false positives - Checked unit tests : 3559 / 3630 test pass (98% pass) - Tokenizer Support for Virtualmethod and imports from traits - Refactored Usenamespace atom - Fixed calculations of fullnspath for static::class - Fixed detection of null/true/false in new() - Added support for T_BAD_CHARACTER ## Version 2.1.5 2020-08-04 - Day Patrol Deity - Architecture Fixed comment size estimation by 1 for T_COMMENT - Added more typehints to code - Report Typehint suggestions : added ticks to fully typed methods - Emissary : Extract more information from dump.sqlite, instead of datastore.sqlite - Ambassador : Added a list of parameters, defined in the application - Ambassador : Added a list of fossilised methods - Stubs : Added check around PHP native functions and CIT - StubsJson : Added property for PHP native structures - Analysis New analysis : Report insufficient initialisation for array_merge() collector variable - New analysis : Report useless triple equals - New analysis : Don't compare typed boolean return values - New analysis : Report wrong type used with PHP functions - New analysis : Suggest abstracting away some PHP native functions - New analysis : Report try block that are too large - New analysis : Report variables potentially undefined in catch clause - New analysis : Report swapped arguments in methods overwriting - Upgraded analysis : InvalidPackFormat speed up - Upgraded analysis : Added parameter to Security/ShouldUsePreparedStatement to choose the preparing method - Upgraded analysis : Added parameter to Security/HardcodedPasswords to choose the name of properties/index - Upgraded analysis : PHP 8.0 new scalar typehint, stringable interface - Tokenizer Added support for named parameters (PHP 8.0) - Trimmed some properties from atoms - Removed non-existent atom mentions - Added support for Attributes (WIP) - Added support for ?-> - Added support for new T_*_NAME tokens ## Version 2.1.4 2020-07-23 - Marshal of Heavenly Blessing - Architecture Added time of last commit in audit results - Added more typehints - Upgraded PHP native method description with typehints (WIP) - Report Typehint suggestion report - New toplogies : call order, - Ambassador : new statistics for typehint usage - Analysis New analysis : Report double assignation of objects - New analysis : Typehints/CouldBe*, which makes suggestions for typehints - New analysis : Checks for argument type when typehint is present in custom methods - Upgraded analysis : Too Many Finds may be configured for threshold and prefix/suffix - Upgraded analysis : Typehints stats were extended to properties and multiple typehints - Upgraded analysis : Global outside Loop is extended to static variable too - Upgraded analysis : ErrorMessages also detect local variable contents - Upgraded analysis : Speed up for NullBoolean, Interfaces IsNotImplemented, InvalidPackFormat, arrayIndex, noWeakCrypto - Checked unit tests : 3532 / 3496 test pass (99% pass) - Tokenizer Removed 'aliased' property in atoms - Fixed spotting of PHP native constants, when in Define() structure - Fixed loading of false values - Added support for the trailing comma in closure's use expression - more handling of phpdocs - Null is now reused when it is a default value, as a typehint. - Logical was split in two : Logical and Bitoperation - Added support for match() {} expression - Fixed boolean calculations during Load - Removed auto-referencing in DEFAULT calculations ## Version 2.1.3 2020-07-02 - Marshal of the Heavenly Canopy - Architecture Removed all usage of datastore in Reports, and only rely on dump. - ignore_rules is now case insensitive - Moved some of the loading to a separate gremlin call to reduce the size of node load. - Fixed the branch option with Git calls. - Storing trait's use expresion's options. - Report Ambassador ; New inventory : PHP protocol used (php, phar, glob://...) - Stubs and StubsJson, have been tested extensively - Analysis New analysis : report double assignations of the same object ($a = $b = new C) - New analysis : report cyclic references - Upgraded analysis : Used Constants edge situations - Upgraded analysis : No real comparison : extended analysis to constants - Upgraded analysis : extended detection of dynamic method calls to call_user_func* - Upgraded analysis : paths are detected with new functions - Checked unit tests : 3490 / 3520 test pass (99% pass) - Tokenizer More phpdoc support (from code to report) - Added isPHP to absolute FQN notations ## Version 2.1.2 2020-06-25 - Mountain Deity - Architecture Removed files task from initproject. - Added ignore_rule directive, to ignore specific rules while running a specific report - More documentation (in particular, modifications section) - Exakat avoids to return twice the same results (file and line) - Sped up some analysis, and added a time limit per analysis - Removed double linking for static variables - Report New reports ; Stubs and StubsJson, which produce the stubs of the audited code (PHP and JSON format) (WIP) - New report ; Typehint suggestion (WIP) - Ambassador ; offers the configuration for all the rules that spotted issues in the current audit, for reuse in other codes - Collect the number of property per class - Analysis New analysis : Report methods that are too much indented on average - New analysis : Report possible confusion between a class and an alias - New analysis : Report variables that are static and global at the same time - New analysis : Report statement with long blocks - New analysis : Report phpdoc's deprecated methods and function calls - Upgraded analysis : Dereferencing levels now include () and = - Upgraded analysis : Unused Methods now skips classes that calls themselves dynamically - Upgraded analysis : No Need Get_class() was refactored - Upgraded analysis : Avoid Optional Properties was refactored - Upgraded analysis : Variable inconsistent Usage was extended with more reach - Upgraded analysis : Indirect Injections was upgraded with better reach with variables - Upgraded analysis : Direct Injections was upgraded with include - Upgraded analysis : PHP 8.0 new scalar typehint, stringable interface - Upgraded analysis : Mismatch Type and default now avoids undefined constants - Upgraded analysis : Wrong Optional Parameter is upgraded for PHP 8.0 - Upgraded analysis : Indentation level was refactored - Checked unit tests : 3480 / 3510 test pass (99% pass) - Tokenizer Upgraded detection of PHP native constants, when they are in absolute notation - Dump task stores use expressions' options, plus minor fixes - Added support for Attributes (PHP 8.0) - Added support for Union types (PHP 8.0) - AtomIs step (WITH_VARIABLE) was extended with local variables - DEFAULT doesn't point anymore on auto-updated values - Extended support for phpdoc in the code - Added support for promoted properties (PHP 8.0) ## Version 2.1.1 2020-06-01 - Earth Deity - Architecture Using timeLimit() to prevent Gremlin from running too deep in the rabbit hole - Added Neo4j Graphson V3 Graph driver - Moved 'Dump' rules to a specific Ruleset for easier administration - Propagated the upgrade to PHP 8.0 union types to three more rules - Fixed access to the list of ignored files - Added support for explicit stub files - Fixed multiple calls to Dump (better reentrant) - Report New report : Meters, which holds measures for the audited code. - Ambassador : inventory of OpenSSL ciphers - Analysis New analysis : Report unused traits - New analysis : Report chmod 777 system calls - New analysis : Check for keylength when generated by PHP - New analysis : Report methods with prefix/suffix and expected typehint - New analysis : Mark classes when they call dynamically their own methods - New analysis : Check for constants hidden in variable names ${X} != $X; - New analysis : Throw will be an expression in PHP 8.0 - Upgraded analysis : Dangling operator now checks for loops too - Upgraded analysis : 'Variables used once' now skips variable definitions - Upgraded analysis : 'Access Private' takes into account dynamic classes - Upgraded analysis : 'Could Centralize' now uses a custom threshold. Default is 8 usage of an expression to centralize. - Upgraded analysis : 'Return true/false' checks that they are alone in the blocks - Upgraded analysis : 'Unreachable code' checks on constants values before reporting the next expression - Upgraded analysis : 'Magic methods' are case insensitive - Upgraded analysis : 'No Hardcoded passwords' has new functions that require a password - Upgraded analysis : 'Unused methods' are omitted for dynamically called methods and overwritten methods - Upgraded analysis : Insufficient Property Typehint also works for untyped properties - Upgraded analysis : PHP 8.0 new scalar typehint, stringable interface - Checked unit tests : 3383 / 3444 test pass (98% pass) - Tokenizer Arguments with null as default values, automatically are nullable - Intval is also an integer for logical operations - Default Values now omits recursives assignations - Fixed fullnspath for PHP short tags - Added link between new command and constructor of anonymous classes. ## Version 2.1.0 2020-05-13 - City God - Architecture results stored in HashResults are now testable - Moved all query methods to Query/DSL namespace, from Analyzer class - Report New report : ClassReview, with focus on classes structures - New report : Typechecks, with focus on type hint usage - Ambassador : Added typehint stats section - Ambassador : fixed display of classes name in classes tree - Ambassador : some missing sections have been rehabilitated - Analysis New analysis : Trailing comma in signature (PHP 8.0) - New analysis : Hidden nullable types - New analysis : Not implemented abstract methods - New analysis : Report confusion between variables and arguments with arrow functions - Upgraded analysis : No literal for reference was extended - Upgraded analysis : Add zero is extended to constants - Upgraded analysis : This is for classes is now valid with arrow functions - Upgraded analysis : Useless arguments takes also into account constants - Upgraded analysis : Wrong Type With Call supports variadic arguments - Upgraded analysis : Extension constants now support fully qualified names - Upgraded analysis : Bad Typehint relay is compatible with union types - Upgraded analysis : Multiple Identical Cases now handles constants too - Checked unit tests : 3437 / 3477 test pass (99% pass) - Tokenizer Restored 'List' atom - Interface methods are now 'abstract' by default - Added 'array' typehint for variadic arguments - Distinguish between argument and local variable in fn functions - Removed nullable property - propagate calls now propagates closures and arrow functions - Added support for union types (PHP 8.0) - Check all error messages from php, not just the first ones ## Version 2.0.9 2020-04-30 - Jialan - Architecture Added option in TU for analysis that won't fill the result table. - Reduced the number of duplicate links in the graph - Upgraded tokens for PHP 8.0. - Analysis New analysis : Don't collect void - New analysis : Wrongly inited properties - New analysis : Not inited properties - Upgraded analysis : PHP 8.0 removed functions - Upgraded analysis : Useless instructions also include global/static variables - Upgraded analysis : Bad Relay Function now works with return types and property types - Upgraded analysis : 'Scalar or object properties' are upgraded with static calls - Removed analysis : Classes and Arrays IsRead and IsModified. Use properties now. - Checked unit tests : 3347 / 3420 test pass (97% pass) - Tokenizer Fixed edge case for xor, with intval - Refactored multiple calculation for cast values - Added support for links between constants and use expressions - Linked classes with calls, when using use expression ## Version 2.0.8 2020-04-20 - Ao Run - Architecture Added new information in dump.sqlite, to make report autonomous - Analysis Upgraded analysis : Paths are also recognized with constants, and more functions - Upgraded analysis : Should Use single Quotes - Checked unit tests : 3328 / 3398 test pass (97% pass) - Tokenizer Fixed detection of PHP constants ## Version 2.0.7 2020-04-14 - Ao Shun - Architecture Adopted strict_types - Removed ctype1 attribute - Moved linting into separate processes - Refactored analysis to export to dump via SQL - Added 'None' ruleset to Dump task - Report Ambassador : Added Constant's order report - None : Added support for No report - Analysis Upgraded analysis : Undefined class constants - Upgraded analysis : Undefined global constants - Upgraded analysis : Undefined property - Checked unit tests : 3347 / 3420 test pass (97% pass) - Tokenizer Support PHP 8.0's tokens - Added support for multiple typehint in the engine - Fixed edge case for boolean type casting ## Version 2.0.6 2020-03-04 - Ao Qin - Architecture Refactored analysis types for first UT - Moving to PHP 7.4 by default - Report Rector : added more coverage - All : better display of typed properties - Analysis New analysis : Semantic names of arguments - New analysis : !$a == $b - New prototype : possibles interfaces - Upgraded analysis : Overwritten literals now skips .= - Upgraded analysis : Scalar or object handles return type - Checked unit tests : 3322 / 3420 test pass (97% pass) ## Version 2.0.5 2019-11-25 - Ao Guang - Architecture Fixed access to severity and timetofix from compiled extension - Report Ambassador : Fixed links to documentation - Analysis Upgraded analysis : Mismatched Type and Default now omit undefined constants - Checked unit tests : 3366 / 3402 test pass (99% pass) ## Version 2.0.4 2019-11-18 - Army Defeating Star of Heaven's Gate - Architecture Reducing Analyzer's class method count - Moving more collections to Dump/ and Complete/ - Report Rector : added more coverage - Ambassador : Skiped analysis are now reported, not with -1 - Ambassador : Foreach favorites's graph is displayed - Ambassador : Visibility suggestion has full method names - Analysis Upgraded analysis : Don't Mix ++ now skips $a[$b++] - Upgraded analysis : Type hint stats skips some return values - Checked unit tests : 3365 / 3401 test pass (99% pass) ## Version 2.0.3 2019-11-11 - Military Star of the North Pole - Architecture Added check on xdebug presence (nesting limit) - Moving more collections to Dump/ - Analysis New analysis : Nullable typehint requires a test on NULL - New analysis : Typehint that requires too much - Upgraded analysis : Printf check on arguments works with '.' - Upgraded analysis : No magic for arrays skips __get() - Upgraded analysis : Const recommended, but not when methods are used - Upgraded analysis : Written only variables handles compact() - Upgraded analysis : Callbacks need returns, but not for spl_autoload_register() - Upgraded analysis : Extended analysis to Concatenation an Heredoc for Email - Upgraded analysis : Disconnected classes handles case sensitivity - Checked unit tests : 3371 / 3397 test pass (99% pass) ## Version 2.0.2 2019-11-04 - Danyuan Star of Honesty and Chasity - Architecture Adding more typehint - Created new class to build Dot files - Cleaned double examples - Dump handles multiple definitions for constants, class, trait, functions. - Report Added new Topology report - Added new Type hint topology sort - Stubs : added class constant visibility - Analysis New analysis : Report argument whose name clashes with typehint - New analysis : Report properties that are insufficiently typed - Moved 'Inclusions' to Dump/ - Added steps to find original and relayed arguments - Tokenizer Fixed paralellisation bug in Load ## Version 2.0.1 2019-10-28 - Military Star of the North Pole - Architecture Added more return type - Centralized reading for ini or json - Report Ambassador: fixed Foreach favorites - Ambassador: added sort to number of parameter list - Checked unit tests : 3345 / 3377 test pass (99% pass) - Analysis Upgraded xmlwriter to json ## Version 2.0.0 2019-10-21 - Civil Star of Mystery and Darkness - Architecture Manual file/line fixes - More simplifcations in load step - Report Ambassador : fixed performance display - Ambassador : report list of shell commands - Typehint4all : first report - Perfile : fixed sorting - Analysis New analysis : Report possible typehint for bool, int, string, array. WIP - Upgraded analysis : common alternatives are extended to switch and elsif - Upgraded analysis : xmlreader description includes class constants, properties and methods. - Upgraded analysis : callback needs return, is extended to php native functions - Checked unit tests : 3345 / 3377 test pass (99% pass) --- # Analysez et corrigez votre code PHP en profondeur avec EXAKAT Source: https://www.exakat.io/analysez-corrigez-votre-code-php-en-profondeur-avec-exakat/ --- # PHP Rss feed Source: https://www.exakat.io/ex/php-rss-feed/ --- # Download and install Community Edition Source: https://www.exakat.io/exakat-community-edition/ # Download exakat --- # How to make an elePHPant Source: https://www.exakat.io/ex/empty/how-to-make-an-elephpant/ # How to make an elePHPant generation # (15/11/2022) This page is a draft. Expect some updates. In case of emergency, contact us directly. # Table of content - Why doing an elePHPant generation - Preparing for an elePHPant generation - Folklore of the elePHPant generation - Miscellaneous info # Content ## Why doing an elePHPant generation? Here are some questions and pre requisite you need to address before starting doing an elephpant generation. No involvment yet (this is the next section), and a lot of fun imagining what it could be. ### What is an elephpant generation An elephpant generation is a batch of elePHPant, the PHP plush toy. The batch has to be 300 or more to be considered a generation, and remembered as this. That way, it means that the elephpant is accessible to the larger PHP community. A generation of elephpant has a PHPather and/or a Phpother. It also has a prototype (see later), which will be known as the first elephpant of the generation. The first elephpant might have slightly distinctive features, compared to the rest of the generation : it is still part of it. ### Values of the elephpant The value of the elephpant is the reason that motivate its generation. Let's be frank : having a plush toy, however cute it is, is not life achievement. And, indeed, elephpants have had various purposes. Here are a few of them : - Proof of love for the PHP language and its community - To celebrate the last PHP version - Raise money for a specific goal To bring more diverse crowds to conferences - To help promote women in the community - To rent buses and drive attendees to a common event - To finance a local user group - To promote a piece of software, an event, a company, a newspaper - To reward students who achieved their diploma - To hire PHP developers - To bring the tech department behind a common value - [your story] ... Take into account that elephpants are easily recognized as the PHP elephpant. It is best to have a significant relationship with the technology. ### Art of the elePHPant #### Colors, nails and the logo side The most classic elephpant is the (official) blue elephpant, with the PHP logo on the left side, so as to ressemble as much as possible as the PHP logo and the 2D elephpant from Vincent Pontier. The simplest form of customization is to choose a different color for the elephpant fur, and its toe nails. With a little help from a local designer, it is possible to take a picture of an elephpant, check various colors and choose the one that works the best with design specs. As of today, all the colors of the rainbow have been already used : red, orange, yellow, white, black, grey, pink, blue, green, etc. White and blue are the most common, and green is the least common. The left side of the elephpant is traditionaly used to add a logo. The logo is stitched, and made of thread : this means that the logo should be simple, and the number of colors should be kept to a minimum. One or two colors are nice, more may raise the price of the elephpant. The exact color, the size of the fur is usually left to the factory to choose. They might be different from the requested colors, so check those at prototype time. #### Accessories For the most adventurous of us, accessories and advanced features are possible. Simply keep in mind that they will definitely raise the price and the minimum quantity of elephpant to produce. Yet, there are some room for special features. Here are some ideas : - Pointy ears, for the Spoky elephpant - Tusk and long fur for Molly, the mammoth - Glasses for Zend elephpant - Hat for the PHP Yorkshire elephpant - Long hair on the head for the PHP Storm elephpant - Half colors elephpant with #### Artistic review Elephpants have to be reviewed by Vincent Pontier. This is a kind review, aiming at keeping the family of elephpants sharing the same DNA, and family traits. For example, the PHP logo is always on the left side of the elephpant, to be similar to the logo. That stage is quite simple : send a mail to Vincent, with a quick description of your intended design. When your elephpant is in the right spirit, his review keept it all. ## Preparing for an elePHPant generation This section deals with the realities of making an elephpant generation. Once you have secured the questions of the previous chapter, it is time to make it a reality. This comes with hard question, like money, custom certification or where the hell will I store so many plush toys. Make sure to read the previous chapter first, as this is the non-fun part, with all the potential deal breakers. ### Who to ask about an elephpants generation It is a good idea to get in touch with some people that will help you shape up the generation. They can share experience, recommendations and answer specific wishes. - Vincent Pontier (@elroubio) - Damien Seguy, (@faguo) - Manuel Lemos, (@mlemos) ### Timeline Creating a generation from scratch takes about 6 months (six). This is quite a process, and it will probably be faster than that. Yet, it is recommended to start planning with such a calendar. Here are the various steps that you may encounter along the way. Good news, you're already in the first one! - Prepare the art of the elephpant - Check your target audience and size the generation - Secure the budget - Make a prototype - Validate prototype and start production - Tease the community with the prototype - Finish paiment for production - Wait for delivery - Receive elephpants and celebrate - Ship elephpants to their new families ### Size of the generation The size of a generation is important : so far, 300 elephpants are the lowest minimum to reach a production level. The next important threshold is 1000, where that particular generation is allowed to make the big elephpants. Also, unit price gets lower at that level, and is probably the most interesting. Larger generations of elephpants are possible, yet rare. The record is currently of 6000 in one batch. The size of the generation is important for the budgeting part (see next section), but also for the storage of the elephpants, once they are delivered. Each group of 50 elephpants makes a box, and 8 boxes makes a cubic meter : 1m x 1m x 1m. It looks small, but this means quite a lot of place. ### Budget Budget is quite straightforward when the generation has been sized. Simple, yet not easy. The figure that are offered here are based on estimates, and in July 2022. Use them when working on the idea, but always check them with the actual production site, as they are subjects to fluctuation, including inflation, custom fees, labor easy, international shipping and insurance, exchange rate, etc. Elephpants cost estimation is about 15 euros/USD each, all included. This estimate covers production, royalty fees, shipping by boat, toy certication (Europe CE); it is for normal sized elephpant (aka, not the big ones). It is a good figure for budgeting. Small elephpants batches starts at 300 (three hundreds), and the largest batch ever was 6000 (six thousands). Unit price decreases with the size of the order, and the budget always grow : in case of doubt, stick to a smaller batch. Large elephpants batches need a 1000 elephpant order, and may be made by unit. Each is about 100 euros/USD each. ### Shipping Shipping is usually offered by boat or by air. Air shipping is a lot faster, but also a lot more expensive. Given the timeline, it is usually worth waiting a few weeks more and get the elephpants in a cheaper and greener way. ### Certifications Elephpants are produced to meet the certifications of any world market, in particular the European market (CE marking) and the north american markets. The certification and custom process is part of the shipping, so they are delivered with the valid custom review. Once inside a country, the elephpants may be shipped again anywhere within that country, or any other country with which there is a trade agreement : once the elephpant have been cleared, they are cleared for all commercial partners. This applies with the EC. If part of the elephpant generation has to be dispatched to a remote destination, the same certification process may be required. This is in particular true for large orders : customs will want to see the certification profile again. As for that, it is possible to request for a separate certification at the factory level. This will come as an extra set of document, which, in turn, may be transmitted to any authority. This will save the process of certifying again the elephpant, after the delivery. ### Shipping the elephpants ## Folklore of the elePHPant generation A large part of making an elephpant generation lies in the folklore around it. Planning it, making it, receiving them and shipping them is a long process, with its own specific silly traditions. Here are some of them. ### Delivery In English, the same word is used for both assisting baby birth and reception of goods : [delivery](https://www.merriam-webster.com/dictionary/delivering). So, an elephpant generation has a date delivery to the happy Phpathers and PHPothers : by extension, this is the date the elePHPants are born. ### PHParents : PHPathers and PHPothers Just like elePHPant is build as a porte-manteau from elephant and PHP, a wide range of words are porte-manteau-ed the same way in the PHP world : P's and F's are replaced by PHP, leading to a new, strange yet vaguely familiar word. This applies to the family : whoever is taking responsability of the ### The first elephpant The first elephpant goes by the name of 'Trisaïeul', a French word meaning great-great-grand-parent. Although, this first elephpant also goes as the 'grand-father of all elephpants'. Also noteworthy : there are currently over 50 elephpants generations, which would make that name a long long repetition of 'great'. ### The elePHPant mother Elephpants are known to have one great-grand-father, aka Trisaïeul, the first elephpant ever. NO ONE asks who was the partner of that first elephpant, carrying about 6000 cubs in one pregnancy : just NO ONE. ### Dedicated elephpants Each generation may dedicate elephpants to some outstanding members of the community. For example, the first generation of elephpant was offered to the members of the PHP group, in 2006. Nowadays, Release Master get a PHP 8 inphpinty elephpant, as a token of gratitude. Vincent Pontier is requesting 3 elephpants for his own museum. Besides the last one, there are no special rules to distribute the elephpants : they may be ### Unloading the elephpants The delivery of elephpant is an important moment. There are several large and cumbersome boxes to take out of a truck, and store them in a dry and safe place. It is a tradition to bring everyone involved in a PHP generation to help unload the boxes. It comes from the first delivery of elephpants, where 54 boxes where unloaded by thrown them from one to the other. Even with the right rythm, it ended in a messy pile of card boards. Later, several generations where unloaded manually, with everyone remotely involved with PHP giving a hand. The legendary level was when the delivery truck parked next to the warehouse, but 60 developers showed up to help. In the end, they lined up across the parking, and the elephpants boxes went the really long way into the warehouse. Also, it rained, that day. Of course, there may also be a warehouse dock and a forklift available, and it will do the job fast and clean. It's just not fun, but go ahead. ### Washing the elephpants It is possible to wash the elephpants, using every day appliance. Make sure to use a very gentle washing program, and low temperature. Also, after drying, you may have to work the plush inside the elephpant to spread it around its body : no need to open the sewings, pressing and massaging the content from the outside is sufficient. Long, but sufficient. ### Customizing further the elephpants Once the elephpant have been delivered, they might be further processed. Being material and thread, it is possible for a skilled couturier or seamstress to add extra features, or an accessory. By experience, it becomes quickly an expensive endeavour, as each of the elephpant has to be processed manually. Besides, even if they are industrially produced, their plush and fabrics nature makes them all distinct. Simple operations, such as adding a sticker on the elephpant, or pairing it with a hat are possible and reasonable. ### Shipping the elephpants ## Miscellaneous info ## elePHPants and elePHPants ElePHPants actually have two meaning : - The original drawing by Vincent Pontier, and all its derivatives - The plush toy elePHPant, based on Vincent Pontier drawing, and produced by Damien Seguy. Although both notions are close, they are two distinct art productions. ## Special cases and accomodations Of course, each elephpant generation is different, and may be prepared in a slightly different way from the others. Half production by boat, and half by plane? Biological fur or multi-colored toe-nail? Each in a box or all under a vacuum ? Local factory or remote one? Extra certifications ? While this guide get you through a lot of details, some of those have to be discussed directly with Vincent Pontier or the production facility. Some are possible, others cost more and some are not possible : if you have a special idea for your elephpant, come and discuss it, we'll do our best to have it done or find an alternative. --- # PHP is alive and trumpetting Source: https://www.exakat.io/ex/php-is-alive-and-trumpetting/ PHP is alive, kicking and trumpetting. Here are a few reasons why, and their link.           - 2022 [PHP Foundation: Alive and Kicking](https://blog.opencollective.com/php-foundation-alive-and-kicking/) - [Why use PHP is 2022](https://www.youtube.com/watch?v=X5lxnlcjrWM) (video) - 2021 [The State of PHP in 2021: Development Trends and Projections](https://www.zend.com/blog/state-php-2021) - 2020 [PHP is very much alive and doesn’t plan on dying](https://devm.io/php/php-alive-169045) - [PHP turns 25](https://www.jetbrains.com/lp/php-25/) - 2015 [PHP is 20!](https://mwop.net/blog/2015-06-08-php-is-20.html) --- # PHPtip tweets Source: https://www.exakat.io/ex/phptip-tweets/ # PHP tips A pot-pourri of 8 PHP tips that were published with [@exakat](https://twitter.com/exakat) account. --- ## strict_types exceptions strict_types do not apply to #PHP operators, only on to typed structures. Here, concatenation and interpolation all call __toString(), but not foo(). As you can see, print() and echo() are safe too, while implode() is not. ![](https://www.exakat.io/phptip/phptip-1.png) [Tweet](https://twitter.com/exakat/status/1571452922042650624) --- ## __invoke() and properties and methods What does this tricky #PHP code displays? ![](https://www.exakat.io/phptip/phptip-2.png) [Tweet](https://twitter.com/exakat/status/1569711228301496320) --- ## Method syntax and __invoke() There are some other cases around instanceof, which are surprising upon first read. We can use a string in a variable, but not a direct string, a constant nor a ::class. ![](https://www.exakat.io/phptip/phptip-3.png) [Tweet](https://twitter.com/exakat/status/1569598507329232896) --- ## object is not a type Such situations always make me smile, yet I am certain several of us will loose time on such a mistake. ![](https://www.exakat.io/phptip/phptip-4.png) [Tweet](https://twitter.com/exakat/status/1568871734962720770) --- ## PHP keywords and namespaces I was today's old when I realized that #PHP keyword are allowed in namespaces name since #PHP 8.0. This goes to PHP 8.0 Compatibility ruleset. ![](https://www.exakat.io/phptip/phptip-5.png) [Tweet](https://twitter.com/exakat/status/1567402995800805378) --- ## PHP silent optimisation PHP optimisation in action : undefined variables are only reported when they are used. first is omitted : no operation second is skipped : no need to execute 2nd term third is reporting a warning. ![](https://www.exakat.io/phptip/phptip-6.png) [Tweet](https://twitter.com/exakat/status/1563547525688147968) --- ## PHP recycles TIL PHP recycles the previously created stdClass objects. The following code returns Object #1, until it is stored in $b. TIL (2) : PHP's stdClass's constructor omits all its arguments. ![](https://www.exakat.io/phptip/phptip-7.png) [Tweet](https://twitter.com/exakat/status/1557621083615813632) --- ## When integer overflows Mathematics have the 'Ramanujan Summation', where the sum of all integers is -1/12. #PHP has the integer overflow. Stay away from the PHP_INT_MAX limits. Valid with (int) or intval() with recent #PHP versions. ![](https://www.exakat.io/phptip/phptip-8.png) [Tweet](https://twitter.com/exakat/status/1552896398152048640) --- --- # Blog Source: https://www.exakat.io/blog/ --- # Graphs Source: https://www.exakat.io/graphs/ # Illustrated technology Some everyday situations, displayed as a graph with emotions. ## Experience vs amount of code The initial tutorial is well crafted, and lead to an minimal amount of code. At the first autonomous usage, one will also onboard his old habits, and take many detours to achieve a simple goal. The first rewrite makes it better, until the epiphany, well all the actual concepts are suddenly understood. This improves over time, until one is able to redo the initial tutorial. Date : Jun 18th, 2022   ## Technology lifecycle How technology evolve from a need, get more attention until it spawns a new solution, which disappears once it is a routine. Date : Jun 12th, 2022   ## Moral during code migrations The ups and downs of running a code migration. It is a constant swing between excited enthusiasm and sudden strokes of despairs. In the end, it works. Date : Apr 12th, 2022   ## Unit tests failing and stres levels A few tests failing is a healthy sign. Large amount of them failing is an entire failing feature, and will probably be resolved at once; all of them failing means the unit test system is broken. Date : Mar 12th, 2022   --- # Inphpinity Checkout Source: https://www.exakat.io/inphpinity-checkout/ --- # Inphpinity Account Source: https://www.exakat.io/inphpinity-account/ --- # Refund and Returns Policy Source: https://www.exakat.io/ex/refund_returns/ **This is a sample page.** ### Overview Our refund and returns policy lasts 30 days. If 30 days have passed since your purchase, we can’t offer you a full refund or exchange. To be eligible for a return, your item must be unused and in the same condition that you received it. It must also be in the original packaging. Several types of goods are exempt from being returned. Perishable goods such as food, flowers, newspapers or magazines cannot be returned. We also do not accept products that are intimate or sanitary goods, hazardous materials, or flammable liquids or gases. Additional non-returnable items: - Gift cards - Downloadable software products - Some health and personal care items To complete your return, we require a receipt or proof of purchase. Please do not send your purchase back to the manufacturer. There are certain situations where only partial refunds are granted: - Book with obvious signs of use - CD, DVD, VHS tape, software, video game, cassette tape, or vinyl record that has been opened. - Any item not in its original condition, is damaged or missing parts for reasons not due to our error. - Any item that is returned more than 30 days after delivery ## Refunds Once your return is received and inspected, we will send you an email to notify you that we have received your returned item. We will also notify you of the approval or rejection of your refund. If you are approved, then your refund will be processed, and a credit will automatically be applied to your credit card or original method of payment, within a certain amount of days. **Late or missing refunds** If you haven’t received a refund yet, first check your bank account again. Then contact your credit card company, it may take some time before your refund is officially posted. Next contact your bank. There is often some processing time before a refund is posted. If you’ve done all of this and you still have not received your refund yet, please contact us at {email address}. **Sale items** Only regular priced items may be refunded. Sale items cannot be refunded. ## Exchanges We only replace items if they are defective or damaged. If you need to exchange it for the same item, send us an email at {email address} and send your item to: {physical address}. ## Gifts If the item was marked as a gift when purchased and shipped directly to you, you’ll receive a gift credit for the value of your return. Once the returned item is received, a gift certificate will be mailed to you. If the item wasn’t marked as a gift when purchased, or the gift giver had the order shipped to themselves to give to you later, we will send a refund to the gift giver and they will find out about your return. ## Shipping returns To return your product, you should mail your product to: {physical address}. You will be responsible for paying for your own shipping costs for returning your item. Shipping costs are non-refundable. If you receive a refund, the cost of return shipping will be deducted from your refund. Depending on where you live, the time it may take for your exchanged product to reach you may vary. If you are returning more expensive items, you may consider using a trackable shipping service or purchasing shipping insurance. We don’t guarantee that we will receive your returned item. ## Need help? Contact us at {email} for questions related to refunds and returns. --- # Changelog (up to 2.0) Source: https://www.exakat.io/ex/changelog-2-0/ Note : the [more recent changelog](https://www.exakat.io/en/exakat-changelog/) is here. ## Version 2.0.0 2019-10-21 - Civil Star of Mystery and Darkness - Architecture Manual file/line fixes - More simplifcations in load step - Report Ambassador : fixed performance display - Ambassador : report list of shell commands - Typehint4all : first report - Perfile : fixed sorting - Analysis New analysis : Report possible typehint for bool, int, string, array. WIP - Upgraded analysis : common alternatives are extended to switch and elsif - Upgraded analysis : xmlreader description includes class constants, properties and methods. - Upgraded analysis : callback needs return, is extended to php native functions - Checked unit tests : 3345 / 3377 test pass (99% pass) ## Version 1.9.9 2019-10-14 - Lasting Prosperity Star of True Man - Architecture Documentation review - Report New reports : Stubs, Rector - Typehint stats - Stubs takes into account use expression - Added Concrete5 and Typo3 as vendors - Analysis New analysis : checks on is_a third argument - New analysis : Invalid mbstring encodings - New analysis : Weird Index in arrays - New analysis : Avoid FILTER_SANITIZE_MAGIC_QUOTES - New analysis : Don't forget third argument - New analysis : Hard to update methods - New analysis : Merge two ifthen into one - New analysis : Report wrong type with calls - New analysis : Check case for namespaces - Updated analysis : Undefined interfaces now includes interfaces extensions - Updated analysis : Report more wrong types with return type - Updated analysis : Register globals also applied to class - Updated analysis : Could Use Try covers more new, functions and static calls - Updated analysis : Useless Cast also reports (string) array (always Array) - Checked unit tests : 3343 / 3366 test pass (99% pass) - Tokenizer Create default values for foreach - Load captures empty files, and omit them - Create default values also handles ??= ## Version 1.9.8 2019-10-07 - Giant Gate Star of Dark Essence - Architecture Upgraded dump command to handle multiple -P - .yaml configuration handles multiple reports - Started journey to strict_types - Code cleaning - Report Ambassador : Fixed report of Flexible Docs - Ambassador : trimmed delimiters in inventories - Inventory : Foreach, with key values - Analysis New analysis : Wrong case for functions - New analysis : Parameter Hiding - New analysis : Report usage of Traversable - Updated analysis : Undeclared properties skips undefined properties - Updated analysis : Useless Interface, modernized query - Updated analysis : String Holding Variables now skips default, const, sprintf - Updated analysis : Binaries are not confused with hex - Updated analysis : Extended 'Insufficient typehint' to abstract classes - Checked unit tests : 3324 / 3343 test pass (99% pass) - Tokenizer Fixed handling of large powers - Added more escaping when storing to SQLITE ## Version 1.9.7 2019-09-30 - Greedy Wolf Star of Sunlight - Architecture Added support for analysis reporting missing values in a reference list - Fixe batch dumping of results - Report Ambassador : new inventory : dereferencing levels - Analysis New analysis : Use PHP Native URL parsing functions - New analysis : Maximum dereferencing level - New analysis : Use case value in a switch : it was already tested - Updated analysis : No class as typehint accepts abstract classes - Updated analysis : Create Magic Property reachs out to traits - Updated analysis : Security also reports usage of unserialize() - Updated analysis : Mistmatched default argument also covers methods - Updated analysis : Never used parameter also covers methods - Updated analysis : Unused global also cover static variables - Updated analysis : Duplicate strings threshold is not 15, not 5. - Checked unit tests : 3289 / 3319 test pass (99% pass) - Tokenizer RETURNTYPE, TYPEHINT, and DEFAUT are not always on, with Void atom, or better. - DEFAULT value targets end-values, skips ??, ?:, () and =. - Exceptions now reports errors in the Query, not where it is thrown ## Version 1.9.6 2019-09-23 - Star of Birth - Architecture Moved new elements to Complete/ - Moved new elements to Dump/ - Initial configuration of project now includes analysis parameters with default - Added descriptions to Rulesets - New command Config : displays current configuration for reuse and editing - Upgraded Doctor : support for docker-php, in-code - Report Ambassador : removed {} on magic property inventory - Ambassador : new inventory of network protocols used (udp://, ssh2://...) - Analysis New analysis : avoid mb_string inside loops - New analysis : avoid SSLvx and TLSv1.0 - New analysis : report duplicate literal in the code, with parameter - New analysis : warn about null property - New coverage : calls to __call and __callStatic - Updated coverage : expressions with parenthesis - Updated coverage : default values are now targeting the final value in multiple assignations. - Updated analysis : Strange Variable name skips Staticdefinition and its default value - Updated analysis : Useless instructions are upgrade with pure functions - Updated analysis : Extended Closure2string with Arrowfunctions - Updated analysis : Extended 'Could be local variable' to traits - Updated analysis : Unused Global also covers static variables - Checked unit tests : 3279 / 3304 test pass (99% pass) - Tokenizer Updated tokens for PHP 7.4 ## Version 1.9.5 2019-09-16 - Star of Adversity - Architecture Added count property to Analysis node, stepstone for Diff analysis - Added support for 'optional' step - Added support for 'interfaces' as typehint for remote definitions - Removed more true/false values - Fixed strtolower with mb_strtolower in Dump - Report Added several PHP error messages - Ambassador : added inventory of magic properties - Ambassador : added inventory of typehints for methods (WIP) - Added support for function/closure/argument arguments - Added support for function/closure/argument arguments - Analysis New analysis : No literal value as referenced argument - New analysis : use array_slice or array_splice - New analysis : Useless typechecks with Typehint - New analysis : Report non-implemented interfaces - New analysis : Incompatible Signatures with Self (PHP 7.4+) - New analysis : Report wrong expectations from interfaces - Upgraded analysis : Excluded __construct and __destruct from Magic Methods - Upgraded analysis : Concat and Addition : Now also for bitshift - Upgraded analysis : Incompatible Signatures with Self (PHP 7.3) - Upgraded analysis : Elseif and Sequences are omitted in Level analysis - Tokenizer Upgraded support for magic properties ## Version 1.9.4 2019-09-09 - Star of Benefit - Architecture Dump avoid storing multiple definition for the same class - Added more native return definitions - Adding UT for Complete/ - Dump inventories are being moved to analysis class - Moving more Themes => rulesets - Report Ambassador : Fixed several internal links - Ambassador : Displays the levels of nesting in the code - Ambassador : Upgraded compatibility report with PHP 7.4 - New report : Stubs - Analysis New analysis : PHP 7.4 New Directives - New analysis : Too many dimensions with array - New analysis : Check concat and coalesce precedence - New analysis : Adopt explode() third argument - New analysis : Ternary and useless assignation - New analysis : Nested ternary without parenthesis - New analysis : Spread operator with arrays - New analysis : Max level of indentation - New analysis : Use Arrowfunctions - Upgraded analysis : Clone with non object handles containers - Upgraded analysis : Calling non-static methods statically - Upgraded analysis : Unresolved Instanceof - Upgraded analysis : Array_merge and variadic, extended to isset - Checked unit tests : 3234 / 3259 test pass (99% pass) - Tokenizer Last element of list() is not omitted anymore ## Version 1.9.3 2019-09-02 - Star of Longevity - Architecture Created new Complete category, with data complement for analysis - Refactored constant propagation - Made code compatible with PHP 7.4 - Rename project_themas to project_rulesets - Added support of -p with .exakat.yaml - Report Ambassador : reworked presentation for visibility suggestions - Analysis New analysis : report covariance and contravariance for compatibility - New analysis : no spread operator for hash values - New analysis : self-closing tags are omitted by strip_tags - New analysis : report Openssl_random_pseudo_byte second argument usage - New analysis : CURLPIPE_HTTP1 is obsolete - New analysis : removed PHP 7.4 directives - New analysis : do not use ... with array_merge without checks - Updated analysis : added crc32c as hash algorithm - Removed analysis : Removed Curly Arrays (double take) - Checked unit tests : 3219 / 3240 test pass (99% pass) - Tokenizer Extended OVERWRITE to Interfaces - Extended support for class_alias() ## Version 1.9.2 2019-08-26 - Star of Prosperity - Architecture Introduced a new set of analysis : Complete - Cleaned code for PHP 7.4 usage - Refactored Query to skip impossible Gremlin calls - Now using Project for project names - Report New report : classes dependencies (HTML version) - New report : files dependencies (HTML and DOT version) - Ambassador : datas -> data - Analysis New analysis : {} are deprecated in PHP 7.4 - New analysis : Don't use ENT_IGNORE - New analysis : fn is a PHP 7.4 keyword - Updated analysis : Functions/UseConstantAsArguments covers also password_hash() - Updated analysis : printf arguments now handles positional formatters - Checked unit tests : 3172 / 3199 test pass (99% pass) - Tokenizer Fixed precedence for left associativity ## Version 1.9.1 2019-08-19 - Star of Life - Architecture Fixed zip as code source - Report Ambassador : Fixed issues list for Favorites - Owasp : switched dashboards - Analysis Updated analysis : Loop Calling got one extra check - Checked unit tests : 3148 / 3187 test pass (99% pass) ## Version 1.9.0 2019-07-29 - Ming Wenzhang of Jiayin - Architecture Added missing configuration file for tinkergraph 3.4 - Upgraded support for running exakat with PHP 7.4 - Analysis New analysis : array_key_exists() now report object usage - New analysis : report mb_strrpos 4th argument - New analysis : Reflection export are deprecated - New analysis : Report classes without parents but with 'parent' - New analysis : Don't use scalar as arrays - New analysis : Report use of PHP 7.4 serialize method - Updated analysis : Multiple Identical Keys checks for undefined keys first - Updated analysis : Dont be too manual : extended to catch clauses - Updated analysis : setcookie detection anchors the keyword at the beginning of the string - Updated analysis : Failed Substr comparison now works with constants - Updated analysis : Added support for continue 2 and 3 - Checked unit tests : 3147 / 3186 test pass (99% pass) - Tokenizer Added support for __serialize and __unserialize - Added support for numeric literal separator - Skip entirely unparsable files ## Version 1.8.9 2019-07-22 - Meng Feiqing of Jiachen - Architecture Check on graphdb configuration : default to nogremlin - Added support for baseline for project and report - Moved more doc to ruleset - Check on .git folder for update - Added -version option for upgrade command - Doctor honors .exakat.yml file - Analysis New analysis : Report useless type of checks - New analysis : Disconnected classes - New analysis : Avoid using mb_detect_encoding() - New analysis : Check that source and blind variables are different in foreach - New analysis : ~ or ! favorite - Updated analysis : Is Zero omits multiplications - Updated analysis : Used Private Property is upgraded - Updated analysis : Multiple Identical Keys : refactored - Updated analysis : Undefined variables now skips extract, include, eval - Checked unit tests : 3147 / 3166 test pass (99% pass) - Tokenizer Refactored support for Foreach : each blind variable is in VALUE - Upgraded precedence for ! (not) - Propagate constants with assignations - Fixed link to $this inside heredoc and co - Fixed an edgecase where Static method call was confused with Newcall ## Version 1.8.8 2019-07-15 - Wei Yuqing of Jiawu - Architecture Modernized tinkergraph support - When pcntl is available, stubs are produced in a child process - Removed duplicated methods - Exported sequences to helpers - More UT libraries are supported - Federated BUSYTIMEOUT in constant - Report Ambassador and all dependend reports were refactored : menu is configurable with Yaml - Emissary is the upcoming configurable report. - Analysis New step : Load data from code - New analysis : Variables used for setting aside value temporarily - New analysis : Use PHP array_* functions, instead of loops - Updated analysis : Unused methods now skips methods from PHP native interfaces (Arrayaccess) - Updated analysis : No class for typehint is now omitting PHP and extensions classes - Updated analysis : Switch to Switch applies to comparisons now - Updated analysis : Close namingg was sped up significantly - Updated analysis : array_column() suggestion was refined - Updated analysis : Htmlentities parameters also support some parenthesis usage - Updated analysis : Constant Scalar Expression only target specified expressions - Updated analysis : Static Properties skip Virtual properties - Checked unit tests : 3131 / 3155 test pass (99% pass) - Tokenizer Refactored support for Exit and Die - Added raw support for phpdoc ## Version 1.8.7 2019-07-08 - Hu Wenchang of Jiashen - Architecture Added bugs fixes up to 7.3.7 - New factory method for the graph - Analysis New analysis : Backward compatible check on generators (can't return) - New analysis : Report wrong return typehint - New analysis : Use DateTimeImmutable - New concept : Methods that throw errors - Updated analysis : Recursive functions disambiguate methods - Updated analysis : Refactored property/variable confusion - Updated analysis : Could typehint checks on type validations - Updated analysis : Variable used once check for abstract methods - Updated analysis : Array_merge in loops omits file_put_contents() - Updated analysis : Simple Regex covers all special sequences, and unicode sequences - Checked unit tests : 3131 / 3142 test pass (99% pass) - Tokenizer Differentiated support for self and static in calls - Moved Symfony support to its extension - Reworked loading to make it parallels. ## Version 1.8.6 2019-07-01 - Wei Yuqing of Jiawu - Architecture Added support for Tinkegraph 3.4 - Extended support for Dev - Renamed Themes to Ruleset (WIP) - Split several long running queries into smaller chunks - Cached files to memory, write them once only - Optimized sides queries : omitting them when possible - Added count of issues in Analyse node - Optimized loading by grouping by inV - More coverage for Arrowfunction - Report Dump : collect PHP cyclomatic complexity - Analysis New analysis : Dependant abstract classes - New analysis : Don't use Null or Boolean as an array - New analysis : Infinite recursion - Updated analysis : Raised levels - Updated analysis : Method signature must be compatible - Updated analysis : Access Private in Trait is OK - Updated analysis : Recursive function - Checked unit tests : 3099 / 3105 test pass (99% pass) - Tokenizer Upgraded support for 'Modules' ## Version 1.8.5 2019-06-24 - Zhan Zijiang of Jiaxu - Architecture Fixed several bugs in the online documentation - Started removing analysis, replacing with analysis - Fixed path in docker PHP usage. - Report Ambassador : Export full INI and YAML config to replicate audit - Analysis New analysis : Unused class constants - New analysis : Could Use available Trait - New analysis : literal that Could Be Constant - Updated analysis : Access Private in Trait is OK - Updated analysis : multiple identical argument is extended to closures, methods - Updated analysis : ext/rdkafka - Updated analysis : No Hardcoded Hash is accelerated - Updated analysis : Extended printf() check to constants - Updated analysis : Optimized 'redefined method' - Updated analysis : Memoize Magic Call - Updated analysis : set_locale requires constants - Checked unit tests : 3099 / 3105 test pass (99% pass) - Tokenizer Added missing isModified to Foreach keys - Class Method Definition handles old style constructor - strict_types don't yield a block - Added typed values for magic constants - Refactored new -> constructor link for Self, Static, parent - Added missing arguments count to Newcall ## Version 1.8.4 2019-06-17 - Wang Wenqing of Jiazi - Architecture Added support for PHP in docker images for compilation tests - First prototype for Gremlin in a specific docker image - Report Ambassador : restored original URL - Replaced 'Complexity' => 'Time To Fix' - Replaced 'Receipt' => Ruleset - Analysis New analysis : regex with arrays - New analysis : Complex property names - New analysis : array_key_exists speed up - New analysis : curl_version forbidden argument - New analysis : PHP 7.4 new functions, classes and constants - Fixed analysis : Long Variable - Updated analysis : printf() format check extended to constants - Updated analysis : Written only variables is extended to static and global - Updated analysis : refactored 'Make default' - Updated analysis : 'Wrong number of arguments' is extended to methods - Updated analysis : 'Use coalesce' checks for - Updated analysis : Refactored 'Nested ifthen' to have a parameter - Updated analysis : Extended 'Class Usage' to return typehint - Updated analysis : Sped up 'Used Classes' - Checked unit tests : 2993 / 3071 test pass (97% pass) - Tokenizer Upgraded handling of declare with strict_types - Support for magic properties across classes and traits - Added support for parent with properties - Properties are handled with static and normal at the same time - Fixed virtualproperties with static keyword (self and parent are ok) - Added argument count for 'new A', without parenthesis - Restored old break behavior for PHP 5 and older. ## Version 1.8.3 2019-06-10 - Jade Man of Yang - Architecture Extension docs show version numbers - Manual uses internal links - Report New report : SARB - Updated report : Ambassador list number of arguments in natural order - Analysis New analysis : from substr() to trim() - New analysis : suggest making magic property a concrete one (2 ways) - New analysis : no array auto-append - Updated analysis : 'Scalar or object property' refactored - Updated analysis : 'Multiple identical keys' get a new check on intval, broadened to constants - Updated analysis : 'Indirect injection' accelerated - Updated analysis : 'Could be class constant' accelerated - Updated analysis : 'Never used property' refactored - Updated analysis : 'Modern empty' modernized and broadened - Updated analysis : 'Useless check' skips isset/empty as they may be useful - Updated analysis : 'Identical methoods' skips abstract methods - Updated analysis : 'No Count Zero' also uses sizeof(), skips switch() - Checked unit tests : 2993 / 3071 test pass (97% pass) - Tokenizer Upgraded local definitions for properties to Load phase - Handle static keyword in closures - Moved 'Real' to 'Float' - Created 'Scalartypehint' atom - Fixed intval, boolval for \true and \false ## Version 1.8.2 2019-06-03 - Zhao Ziyu of Dingchou - Architecture Refactored 'Update' command, to VCS - Collect missing definitions counts - Report handles a list of analysis names - Analysis New analysis : No Need To Get_Class - New analysis : Report identical inherited methods - New analysis : Function returning -1 in case of error - Updated analysis : TypeHint must be returned, doesn't apply to abstract methods or interface methods - Updated analysis : 'Could Use Interface' also checks for static and visibility - Updated analysis : 'Concat empty' skips variables - Checked unit tests : 3024 / 3048 test pass (99% pass) - Tokenizer Created 'virtual' properties, for limiting children agglomerations - Fixed normalized code for use traits - Added DEFAULT to all variable definitions - Connect strings to class definitions - Handle variable in 'compact', when they are static ## Version 1.8.1 2019-05-27 - Zhang Wentong of Dinghai - Architecture Fixed Symlink destination - Added collecting classes children, traits and interfaces counts - Added support for constants and functions in modules - Added missing functions in data - Report New report : exakatYaml, which help configuring exakat - New report : Yaml - New report : Top10 - Updated report : Json, text and xml get 'fullcode' - Analysis Updated analysis : Should use self is extended to parent classes - Updated analysis : Should use prepared statement now skips some SQL queries - Checked unit tests : 3024 / 3048 test pass (99% pass) ## Version 1.8.0 2019-05-20 - Zang Wengong of Dingyou - Architecture Added missing native PHP functions - Restored anchor for ignore_dirs[] configuration - Removed more MAX_LOOPING usage - Report Ambassador : removed { & @ } artefacts from globals - Analysis New analysis : Function returning -1 in case of error - New analysis : Report PHP 7.4 unpacking inside array - New analysis : Report PHP 7.4 new functions and fn - New analysis : Useless arguments - New analysis : Addition and concatenation precedence for PHP 7.4 - New analysis : report concatenation of empty strings - New analysis : casting has precedence over ternary - New analysis : report already used traits - New analysis : report missing traits in use expression - Updated analysis : isset on whole arrays : extended analysis to Phpvariables - Updated analysis : SQLITE3 requires single quotes - Updated analysis : Dir then slash : extended to constants - Updated analysis : Variable Strange Name extended to strange types - Updated analysis : Possible interface's analysis is sped up - Checked unit tests : 3021 / 3045 test pass (99% pass) - Tokenizer Fixed fullcode of Usetrait - Extended method definitions to traits - Extended fluent interface detection to parents - Fixed dump for visibility change - Handle method aliases in use expression (as) - Better noDelimiter for double quotes strings ## Version 1.7.9 2019-05-13 - Shi Shutong of Dingwei - Architecture Upgraded list of functions by extension : openssl, math, hrtime - Added global atom to track all globals - Rewrote several Dump queries with DSL - Added support for Notice in Phpexec - Added support for .exakat.ini and .exakat.yaml - Added support for arrow functions : fn => - Added support for spread operator in arrays [...[1,2,3]] - Report Inventories : added 'inclusions' and 'global variables' - Ambassador : added global variables - Analysis New analysis : support for ext/ffi, uuid - Updated analysis : Nested Ternary handles parenthesis - Updated analysis : Static loops is extended to references and arrays - Updated analysis : Recursive function is extended to Magic methods and Closures - Checked unit tests : 3014 / 3019 test pass (99% pass) - Tokenizer Moved 'is_in_ignored_dir' to a property - Cleaned getFullnspath() call in Load - Fixed latent bug on Function fullnspath - Heredoc and Nowdoc are reported as constant if needed - Isset() is not read - Ignore PHP notices when linting - Globals are now centralised across a repository - Extended definitions for Virtualproperties - Removed double DEFINITION link with new ## Version 1.7.8 2019-05-06 - Cui Juqing of Dingyi - Architecture renamed test.php to ut.php in tests - reorganized destinations folders - organized exakat for 'inside code' audit - Analysis New analysis : support for libsvm - Updated analysis : Multiple unset() handles unset() at the beginning of the scope - Updated analysis : undefined static class now accounts for PHP and module classes - Checked unit tests : 2961 / 2995 test pass (99% pass) - Tokenizer Extended class usage to static::class. - refactored 2 analysis for speed : double instruction and double assignations - fixed recent bug where Project token is twice. ## Version 1.7.7 2019-04-29 - Sima Qing of Dingmao - Architecture Upgraded to gremlin-php 3.1.1 - Moved autoload into its own namespace - Started extending themes to modules - Skip external libraries when unit testing - Dump got one more query moved to DSL - Fixed build for overwritten methods, extended to magic methods - Load tokens by batch (5000+ tokens), not by file. - Analysis New analysis : Security : integer conversion - New analysis : implode() with one argument - Updated analysis : Invalid Regex handles \\ more precisely - Updated analysis : delimiter detection was checked for all of them - Checked unit tests : 2947 / 2983 test pass (99% pass) - Tokenizer Upgraded Fallback detection for functions ## Version 1.7.6 2019-04-22 - Jade Maiden of Yin - Architecture Refactored Class definition with return typehint - Added configuration for including development extensions. - Extended LoadFinal typehint hunting - Report Phpcsfixer : new report - Ambassador : report usage of overridden PHP functions - Ambassador : new favorite : variable name in catch clause - Analysis New analysis : array_merge and ellipsis should use coalesce - New analysis : Report overridden PHP native functions - New analysis : Merge all unset() into one - Updated analysis : Added missing constant for curl, pgsql, openssl - Updated analysis : Variadic are not variable arguments - Updated analysis : Useless Reference argument extended to foreach() - Updated analysis : Use Constant also covers pi() - Updated analysis : Inclusion Wrong Case handles dirname with 2nd argument - Updated analysis : Useless Argument : handles some edge cases with arrays - Checked unit tests : 2947 / 2975 test pass (99% pass) - Tokenizer Upgraded handling of isRead and isModified attributes - Changed variadic argument counts in method declarations - Fixed original value in 'Sign' ## Version 1.7.5 2019-04-15 - Xue King Zhuanlun - Architecture Cleaned unused variables - Report Ambassador : bugfixes report version 7.3, dropped 5.6 and 5.5 - Analysis Updated analysis : Already interface : extended to interface parents - Updated analysis : Else if to elseif : extended to one-liners - Updated analysis : No reference for ternary was extended - Updated analysis : Implements is for interface - Updated analysis : Refactored Is a Magic Property - Updated analysis : Refactored Conditional structures for constants - Checked unit tests : 2926 / 2950 test pass (99% pass) - Tokenizer Link properties to magicmethod - Deduplicated virtual properties - Added isRead and IsModified properties. Omitting the corresponding analysis. ## Version 1.7.4 2019-04-08 - Lu King Pingdeng - Architecture reports, themes may be specified multiple times - 'project' command also work on themes and report from command line - Added htmlpurifier in auto-ignored libraries - Counting definitions, omitting Virtualproperties - Automatically detect identical files - Report Inventories are grouped by values, sorted by count - Analysis Updated analysis : This is for class : extended analysis to self and parent - Updated analysis : Undefined Classes - Updated analysis : Refactored Defined Parent MP - Updated analysis : Redefined PHP function is restricted to global scope - Updated analysis : Could Use Alias also covers functions, constants. - Updated analysis : Refined SQL detection - Fixed step : goToALlParentsTrait missed some of the parent - Checked unit tests : 2916 / 2944 test pass (99% pass) - Tokenizer Removed impossible implementations of traits - Fixed functioncalls' 'absolute' property - Refined parent's definitions - Trait also sports virtualproperties - Virtualproperties now respect visibilities - Distinguish Variables from Staticpropertynames - Added missing DEFINITION for Use (namespaces) ## Version 1.7.3 King Dushi, 2019-04-01 - Huang - Architecture New command 'show' that display project creation command - Refactored UT detection mechanism - Report Ambassador : report identical files in the code - Ambassador : global variable inventory is now grouped by name - Analysis Updated analysis : PPPDeclaration style : handles Virtualproperties - Updated analysis : Closure2string : extended analysis - Updated analysis : Non-Ascii variable skips { }, & and @ - Updated analysis : Could Be Static exclude abstract methods - Updated analysis : MismatchedTypehint : handles methodcalls and class hierarchy - Updated analysis : Could Use Try : refined analysis to avoid literals - Updated analysis : Hidden use, handles Virtualproperty - Updated analysis : Classes, wrong case, handles FQN - Checked unit tests : 2846 / 2926 test pass (97% pass) - Tokenizer Moved creation of Virtualproperty early, to catch more situations - Virtualproperty mimic Propertydefinition - Added extra check when roaming the classes tree - Handles Sign constant values correctly ## Version 1.7.2 2019-03-25 - Dong King Taishan - Architecture Restored the external library checker - Added support for extension's CIT (Symfony, Drupal) - Report Ambassador : added Suggestions theme to docs. - Perfile : New report, text, per file - Analysis New analysis : Report potential 'unsupported operand type' - New analysis : Check for existence with __call() and __callstatic - Updated analysis : Wrong number of arguments (methods) upgraded - Updated analysis : Could Be Static ignores empty methods, constants methods - Updated analysis : Added Variable to possibly useless expression - Updated analysis : Constant names are detected based on available noDelimiter - Updated analysis : Abstract classes may have no abstract methods - Checked unit tests : 2889 / 2912 test pass (99% pass) - Tokenizer Added link between __clone and clone - Now handling functions and constants when ignored - Fixed dynamic constants in collector ## Version 1.7.1 2019-03-18 - Bi King Biancheng - Report Ambassador : report lines that concentrate lots of issues - Analysis Extended GoToAllImplements to extended interfaces - Updated analysis : NoScream usage, with authorized functioncall list like fopen - Updated analysis : HiddenUse with support for virtual properties - Checked unit tests : 2867 / 2900 test pass (99% pass) - Tokenizer Added support for 'Virtualproperties' - Harmonized file escaping feature ## Version 1.7.0 2019-03-11 - Bao King Yama - Architecture Added auto-documenting 'ignored' cit to weed out obvious false positive - Report Made Diplomat the default report - Added History report : it stores metrics from audit to audit - Analysis New analysis : Identify self transforming variables ($x = foo($x)) - New analysis : Report unclonable variables - Updated analysis : Undefined Classes, Interfaces and Trait now omit 'ignored' cit from folders - Updated analysis : Inconsistent usage is refactored for properties - Updated analysis : Useless expression, with clone new x - Updated analysis : Only Variable For Reference accepts $this, $_GET - Updated analysis : Lost References was modernized - Checked unit tests : 2854 / 2884 test pass (99% pass) - Tokenizer Refactored support for Staticmethod (in a trait's use) - Added definitions for trait's use ## Version 1.6.9 2019-03-04 - Lu King Wuguan - Architecture Optimized Dump when navigating the links to the File Atom - Refactored LoadFinal into separate classes - Upgraded to Tinkergraph 3.3.5 - Added options to cleandb to stop and start gremlin from exakat - Skip the task if no analysis has to run - Analysis New analysis : Report inconsistent usage of properties or variables - New analysis : Typehinted return must return - Updated analysis : Variables used once handles closure (use) correctly - Updated analysis : Is Zero was refactored partially (WIP) - Updated analysis : Bad Typehint relay got a fix - Updated analysis : Function Subscripting is only suggested for one usage - Updated analysis : Lost References was modernized - Checked unit tests : 2854 / 2881 test pass (99% pass) - Tokenizer Added definition for injected properties - Fixed sack() for subqueries - $this is not a classic variable - Removed double DEFINITION links - Fixed edge case with define() at the end of a script ## Version 1.6.8 2019-02-25 - Yu King Songdi - Architecture Added support for PHP 8.0 - Fixed Constant FNP - Advance progressbar when ignoring files - Report Ambassador : report usage of factories - Collect stats about Foreach usage - Analysis New analysis : Report violation of law of Demeter - New analysis : Report removed constants and functions in PHP 8.0 - Updated analysis : Refactored Nullable Typehint - Checked unit tests : 2851 / 2872 test pass (99% pass) - Tokenizer Fixed edge case for Logical with strings - Reduced max level of looping in GoToAllParents - Distinguish $$ and ${$ ## Version 1.6.7 2019-02-18 - Li King Chujiang - Architecture Documentation covers more PHP functions - Added some missing PHP functions - Fixed destination folder for extensions - Report Ambassador : limited size of default values in visibility report. - Ambassador : reporting class depth - Ambassador : reporting dynamically created constants - Diplomat : leanner, meaner version of Ambassador - New category : Top 10 classic mistakes - Analysis New analysis : Report when relayed typehint are not the sames - Updated analysis : Regex now handles local variables and constants - Updated analysis : Variables Used Once now covers closures and use - Checked unit tests : 2846 / 2867 test pass (99% pass) - Tokenizer Defineconstant may be constant - Fixed handling of Nullable for typehint - Started preparing for Gremlin 3.4.0 : WIP ## Version 1.6.6 2019-02-11 - Jiang King Qinguang - Architecture Removed FetchContext() from DSL - Added options to follow constants from atomIs. - Report Now dumps magic methods - Analysis New analysis : Report insufficient interfaces in typehint - Updated analysis : Class constant now ignore empty classes - Checked unit tests : 2837 / 2858 test pass (99% pass) - Tokenizer Moved 'Define' to its own atom - Upgraded Logical to hanlde Strings as PHP - Fixed T_POWER => T_POW - Refactored calculation for globalpath - Fixed edgecase with endswitch; ## Version 1.6.5 2019-02-04 - Mahagate - Architecture Added CVS as an external service - Graph GSNeo4j export variable for shell access. putenv is not sufficient - Dump : report class name, not its code - Extended listAllThemes to extensions - Fixed bug in extension loader with phar - Report Ambassador : restored file dependencies tree - Ambassador : fixed altered directive filename - Ambassador : added direct link to docs - Analysis New analysis : arrays that are initialized with strings - New analysis : Avoid Lone variables as conditions - New analysis : Added support for weakref and pcov - Updated analysis : extended regex to arrays in preg_* calls - Updated analysis : Implicit globals now also marks the variable in global space - Updated analysis : Add Zero, Multiply by One also cover 2 * $x = 1; - Updated analysis : Could Use Interface now takes into account PHP interfaces, and classes first level. - Updated analysis : Relay Functions now omits calls to parent's __construct and __destruct - Checked unit tests : 2830 / 2852 test pass (99% pass) ## Version 1.6.4 2019-01-28 - Parasamgate - Architecture Added support for CVS as a VCS - Upgraded support for tar as a VCS - Added support modification counts by files - Added first tracking for closures - Upgraded Tinkergraph driver - Report Added Atoms in the documentations - Extra protection for Class Changes - Analysis Updated analysis : Use-arguments are now counted as arguments - Updated analysis : Max Argument check was refactored - Updated analysis : IsModified now takes into account extensions - Updated analysis : Should Use This now exclude empty methods - Updated analysis : undefined classes now support PHP 7.4 typed properties - Updated analysis : added missing scalar PHP types - Updated analysis : uncaught exceptions now cover parents - Updated analysis : refactored incompatibility checks for methods - Checked unit tests : 2824 / 2841 test pass (99% pass) - Tokenizer Refactored alternative ending, removed extra VOID - Upgraded contexts and their nesting - Added extra checks on variables names - Added support for ??= (PHP 7.4) ## Version 1.6.3 2019-01-21 - Paragate - Architecture Better presentation for exakat extensions - Added build.xml for Jenkins - Fixed copyright years - Report Ambassador : fixed class name for Phpcompilation - Analysis New analysis : assign and compare at the same time - Updated analysis : uncaught exceptions now cover parents - Updated analysis : strpos too much is extended to strrpos and strripos - Updated analysis : Refactored Indirect injections for more refined reports - Updated analysis : Empty Block doesn't omit Ifthen anymore - Updated analysis : Implemented methods are public mistook interface methods - Updated analysis : Object Reference omits arguments that are wholly assigned - Checked unit tests : 2808 / 2826 test pass (99% pass) - Tokenizer Added support for PHP 7.4 typed properties (needs PHP 7.4-dev) ## Version 1.6.2 2019-01-14 - Silver Headed Gate - Architecture Fixed infinite loop when an option missed a value - Produce phpversion in config.ini, but leave it commented - Report Ambassador : colored syntax for visibility report - Ambassador : inventory reports now display number of usages - Analysis Updated analysis : Added support for PHP 7.2.14 - Updated analysis : Avoid Using Class handles \ - Updated analysis : Unused Functions works with multiple identical functions - Checked unit tests : 2795 / 2817 test pass (99% pass) - Tokenizer Fixed bug that mixed T_OR and T_XOR - Fixed bug that missed intval for Power - Handles multiple definitions of functions - Removed one Void too many with closing tag ## Version 1.6.1 2019-01-07 - Golden Light Gate - Architecture Upgraded documentation for Extensions - Upgraded processing of files, specially with special chars - Project stops when no token are found - Storing hash for each files. RFU. - Report Ambassador : added support for class constant's changes - Ambassador : added classSize report - Ambassador : 'New issues' now takes line difference into account - Themes are better dumped - Analysis New analysis : array_key_exists() is faster in PHP 7.4 - New analysis : partial report from preg_match() - Updated analysis : Avoid Using Class handles \ - Updated analysis : Class Usage uses class_alias() - Updated analysis : Empty traits - Updated analysis : Unused arguments now skips __set() - Updated analysis : Path strings - Updated analysis : Missing include handles more concatenations - Checked unit tests : 2792 / 2812 test pass (99% pass) - Tokenizer Fixed precedence for identical operators - Fixed bug with ?> inside switch ## Version 1.6.0 2018-12-31 - VirupakSa - Architecture VCS are not tested when they are not used - Analysis Updated analysis : Php Reserved names ignores variable variables - Updated analysis : Array not using a constant, with Heredoc - Updated analysis : Long arguments - Updated analysis : Empty With Expression ignores simple assignations - Refactored analysis : Callback needs returns - Refactored analysis : No Return used - Checked unit tests : 2780 / 2805 test pass (99% pass) - Tokenizer Fixed regression with Yield and => - Fixed edge case "$a[-0x00]" ## Version 1.5.9 2018-12-24 - Dhrtarastra - Architecture Use PHP in project config for default PHP version - cleandb uses -p - Moved projects/.exakat to projects/<-p>/.exakat folders - Using $config and not more hardcoded tinkergraph - Extra check on doctor - Report Ambassador : extra check for 'previous' report - Analysis Upgraded analysis : Empty With Expression skip a few false positive - Checked unit tests : 2770 / 2795 test pass (99% pass) - Tokenizer Fixed edgecase for methods named 'class' - Fixed class name in Project ## Version 1.5.8 2018-12-17 - Virudhaka - Architecture Handles themas provided by extensions - Added busyTimeout for dump.sqlite - Reduced size of thema tables - Docs handle parameter dynamically - Added 'update' for extensions - Report Ambassador : added a 'Path' inventory, with file paths - Analysis New analysis : Closures that are identical - Upgraded analysis : Url and SQL detection, case sensitivity - Upgraded analysis : Could Use array_fill_keys - Upgraded analysis : Undefined functions doesn't miss functions inside classes, handles interfaces - Upgraded analysis : Empty Functions better handles return; - Upgraded analysis : Long Argument may be configured - Upgraded analysis : Fixed bug with empty include path - Checked unit tests : 2770 / 2795 test pass (99% pass) - Tokenizer Added FNP to strings - First link between method and definition with typehint - Support for class_alias - Fixed edge case with use ?> - Fixed variable in string behavior for $this and $php variables ## Version 1.5.7 2018-12-10 - Vaisravana - Architecture Extended Dump to support aliased methods - Support for SQLITE in extensions - Moved each framework to extensions - Added Laravel extension - Documentation First version for the Extension chapter - Fixed mysterious ' in the docs - Report Ambassador : added a 'New issues' section, with new analysis - Ambassador : added trait matrix - Ambassador : fixed an infinite loop when trait include themselves in cycles - Added more message count to several reports - Analysis New analysis : method could be static - New analysis : multiple inclusion of traits - New analysis : avoid self using traits - New analysis : ext/wasm and ext/async - Upgraded analysis : No Hardcoded Hash, skip hexadecimal numbers - Upgraded analysis : Defined properties extends to traits - Upgraded analysis : PSS outside a class, when PSS are in strings - Upgraded analysis : Access private works with methods (not just static) - Checked unit tests : 2772 / 2785 test pass (99% pass) - Tokenizer Fixed bug in Dump, when nothing to clean - Fixed edge bug on Callable detection - Extended support for self, static and parent, in typehint and new - Fixed precedence of yield and yield from - Fixed handling of throw at the end of a script - Added support to solve conflict on traits ## Version 1.5.6 2018-12-03 - Jingang - Architecture Moved all framework to extensions. WIP. - Code cleaning - Refactored the analysis dependency sorting - Now display progress bar for files - Fixed configuration for directories and files - Report Fixed FileDependecy and DependencyWheel, to actually count messages - Analysis Added a lot more new method descriptions for PHP native classes - New analysis : suggestion simplification for !isset($a) || !isset($a[1]) - New analysis : Useless Trait alias - New analysis : report usage of ext/sdl - Upgraded analysis : Refactored IsZero, to handle assignations and parenthesis - Upgraded analysis : pack format is better checked - Checked unit tests : 2759 / 2771 test pass (99% pass) - Tokenizer Fixed a missing fullnspath for origin in Use for Traits - Handles simple aliases for traits methods - Fixed mishandling of variables inside strings - Fixed support of negative numbers inside strings - Fixed bug with yield inside an array - Fixed strange case with define and integers as constant names ## Version 1.5.5 2018-11-25 - Ratnadhvaja - Architecture Initial version of Exakat extensions - Moved processing of 2-tokens files to Load - Speed up CSV creations - Upgrades are read from https, no http - Moved loading's sqlite to memory for speed gain - Doctor now auto-create test folder - Report New report : Php city. See your PHP code as a city - Ambassador : Appinfo() now reports keywords used as method or property - Fixed reported names of properties - Analysis New analysis : checks some HTTP headers for security - New analysis : Use _file() functions, not file_get_contents() - New analysis : Optimize looks for fgetcsv() - Upgraded analysis : Several refactored analysis - Checked unit tests : 3083 / 3096 test pass (99% pass) - Tokenizer Fixed encoding error in loading, for clone types. ## Version 1.5.4 2018-11-19 - Mahakasyapa - Architecture Added error message for memory limit - Added GC to Project action - Migrated Melis to extension - Dumping data is now done en masse - Analysers now handle side-queries - Clear message in case of memory limit - Doctor doesn't stop at missing helpers - VCS leak less errors - Added support for 7z - Extended validation for themas - Restored Tinkergraph driver - Upgrade logs with extra reports - Analysis New analysis : Report problems with class constant visibilities - New analysis : Avoid self, parent and static in interfaces - Upgraded analysis : Variable reuse now skips empty arrays - Checked unit tests : 3077 / 3090 test pass (99% pass) - Tokenizer Fixed bug where variable was mistaken for a string inside strings ## Version 1.5.3 2018-11-12 - Ananda - Architecture Extended results to methods, traits - Added support for PHP 7.2.12 - 'master' is not used anymore as default branch - Fixed creation of initial config/exakat.ini - Fixed handling badly written exakat.ini or PHP binary paths - Report Ambassador : report classes that could be final or abstract - Analysis New analysis : Property Used Once : now includes redefined functions - New analysis : iterator_to_array() should use yield with keys or array_merge() - New analysis : Don't loop on yield : use yield from - Upgraded analysis : Dependant trait now include parent-traits - Checked unit tests : 3080 / 3093 test pass (99% pass) - Tokenizer Changed handling of variable that are both global AND local - Disambiguated variables and properties - Extended OVERWRITE to constants and methods ## Version 1.5.2 2018-11-05 - Master Puti - Report Fixed storage of themes in dump.sqlite - Ambassador : report nothing when there are no trait, interface or class in the tree. - Analysis New analysis : idn_to_ascii() will get new default - New analysis : support for decimal extension - New analysis : support for psr extension - Upgraded analysis : Extended support to PHP native exceptions - Upgraded analysis : Could use typecast now handles intval() second param - Upgraded analysis : Variable strange names avoids properties - Checked unit tests : 3058 / 3085 test pass (99% pass) - Tokenizer Upgraded support for arrays inside strings (string/constant distinction) - Added DEFINITION for constant() and defined() - Fixed value of line for some placeholder definition ## Version 1.5.1 2018-10-29 - Eighteen Arhats - Analysis New analysis : could use basename() second args - Upgraded analysis : Variables strange names do not report ... - Checked unit tests : 3061 / 3079 test pass (99% pass) - Tokenizer Moved TRAILING as a property - Moved NULLABLE as a property - Sync ALIAS with AS - Fixed link between Use expression when using an alias ## Version 1.5.0 2018-10-22 - Pilanpo Bodhisattva - Architecture Fixed " in the examples of the manual - Upgraded stability with new history testing - Report Ambassador : now report interface and trait hierarchy - Ambassador : new format inventory for pack and printf - Dump : Fixed list of traits - Analysis New analysis : Could Use Try, for native calls that may produce an exception - New analysis : idn_to_ascii() will get new default - Upgraded analysis : Undefined variables exclude $this - Upgraded analysis : Variables used once avoid properties - Upgraded analysis : ext/json : JsonException - Upgraded analysis : added new PHP 7.3 constants (curl, pgsql, mbstring, standard) - Upgraded analysis : scalar or object property now ignore NULL as default - Refactored analysis : UsedProtectedMethod - Checked unit tests : 3059 / 3071 test pass (99% pass) - Tokenizer Handles NaN and INF when the literals reach them - Static constant may be variable if object is variable - Removed superfluous linking for static calls. ## Version 1.4.9 2018-10-15 - Lingji Bodhisattva - Architecture Extended documentation with phpVersion, time to fix and severity - Upgraded bufixes to PHP 7.2.11 - Added more tests on arguments in the DSL - Removed double definitions for class constants - Initial support for extension folder - Report Collect the number of local variables, per method - Analysis New analysis : report accessing properties the wrong way - New analysis : suggest named patterns - New analysis : check Pack() arguments - New analysis : Return in generators, for PHP 7.0 + - New analysis : Repeated interfaces - New analysis : Static properties shouldn't use references until PHP 7.3 - New analysis : Don't read and write in the same expression - Upgraded analysis : is interface methods, extended to magic methods - Upgraded analysis : empty regex - Upgraded analysis : never used properties - Upgraded analysis : logical operators in letters - Upgraded analysis : could use interface, extended with PHP native interfaces - Upgraded analysis : Is Zero, better handling of mixed expressions - Refactored analysis : Empty functions - Refactored analysis : Used Private Methods - Checked unit tests : 3036 / 3055 test pass (99% pass) - Tokenizer Added DEFINITION between new and __construct - Added support for className::class() - Added better support for dynamic method calls - Added better support for dynamic property calls - Removed some usage of TokenIs ## Version 1.4.8 2018-10-08 - Ksitigarbha - Architecture Adding more validation at DSL step level : stricter check on args, speed gain - Cleaning more analysis from MAX_LOOPING variable - Better protection for file names - Removed static properties from DSL - Analysis New analysis : Don't use __clone before PHP 7.0 - New analysis : Watch out for filter_input as a data source - Upgraded analysis : Method Used Below refactored for speed - Upgraded analysis : Undefined class constants now takes into account interfaces - Removed anaysis : Relaxed Heredoc was double with Flexible Heredoc - Checked unit tests : 3016 / 3033 test pass (99% pass) - Tokenizer Build links between methodcall and method in a class - Added links between method and its overwritten version in child - Fixed fallback for functions - Fixed linked between traits and their definition - Removed variable definition for Parametername - Simplified double usage between return and pushExpression() ## Version 1.4.7 2018-10-01 - Maitreya - Architecture Added 'Suggestions' section to documentation, for many rules - WIP : removing usage of MAX_LOOPING in analysis - Added a lot of new external services - Added documentation for creating a new analysis - Analysis Upgraded analysis : No interface was dropped in PHP 7.2 - Upgraded analysis : IsAMagicProperty extended to parents - Removed anaysis : Relaxed Heredoc was double with Flexible Heredoc - Checked unit tests : 3017 / 3029 test pass (99% pass) - Tokenizer Linking variable in closure's use to its local variable - Removed some unused atoms from GraphElements ## Version 1.4.6 2018-09-24 - Dipankara - Architecture Various code refactorisations - Migration to PHPUnit 7.3.5 - Fixed filenames case - Better handling of VCS - More validations for project names - More docs - Report Ambassador/Weekly : fixed ' in analyser titles - Analysis Upgraded analysis : Fopen mode accepts 'r+b' - Upgraded analysis : Unused Traits - Upgraded analysis : Undefined Variables - Checked unit tests : 3020 / 3033 test pass (99% pass) - Tokenizer New analysis : report literal used with reference - Added support for boolval to Keyvalue - Fixed support for boolval to Arraylist - Added DEFINITION to static methods - Added Variabledefinition for local variables - Fixed bug in Not ## Version 1.4.5 2018-09-17 - Guanyin Bodhisattva - Architecture Removed times() for until() in Dumps - Report Manual : added folders tree - Analysis New analysis : Add Default To Parameter - Upgraded analysis : Avoid reporting PHP function as classes - Upgraded analysis : More empty Functions than just foo() {} - Upgraded analysis : Wrong Number of argument now takes into account variadic - Upgraded analysis : Should Use Constant now encompasses () and ?: structures - Upgraded analysis : This Is Not An Array now takes ArrayObject/SimpleXmlElement into account - Checked unit tests : 3009 / 3020 test pass (99% pass) - Tokenizer Fixed 'constant' status with Arrayliteral - Fixed bug where strings are build close to the end of the script ## Version 1.4.4 2018-09-10 - White Dragon Horse - Architecture Doctor reports the set of tokens used - Lots of docs checks - Report Ambassador / Phpconfiguration : report disable_functions and disable_classes - Finished Weekly report - Analysis New analysis : report ext/seaslog - Upgraded analysis : Incompatible signatures - Fixed DSL : analysisIs - Checked unit tests : 3000 / 3010 test pass (99% pass) - Tokenizer Closure are now processed with runplugin - Removed depencencies to usedClasses - Fixed detections of Closure at the end of a script ## Version 1.4.3 2018-09-03 - Sha Wujing - Architecture No error if missing svn - Extended 'First' thema - Now reporting PHP native CIT, constants and functions - Report Ambassador : php.ini suggestions includes disable_functions - Analysis New analysis : report typecasting for json_decode - New analysis : report classes that could be final - New analysis : simplify closure into callback - New analysis : report inconsistent elseif conditions - Upgraded analysis : Reduced false positive on Type/Default mismatch - Upgraded analysis : Drop Else After Return uses elsif - Upgraded analysis : Unused Private Property (rare) - Checked unit tests : 2990 / 3004 test pass (99% pass) - Tokenizer Removed extra Void after function definitions - Fixed fullnspath with define() ## Version 1.4.2 2018-08-27 - Zhu Bajie - Architecture Fixed leftover bugs in the new DSL language - Adopter Query in LoadFinal (first test) - Extended support for clone type 1 - Report New Report : Weekly report - Analysis New analysis : report forgotten conflict in traits - New analysis : undefined insteadof - New analysis : undefined variable - New analysis : report classes that must call parent::__construct - Upgraded analysis : Inexistant Compact variable - Upgraded analysis : Test class was refactored - Checked unit tests : 2975 / 2989 test pass (99% pass) - Tokenizer New atom : Staticmethod, for Insteadof (replacing 'Staticconstant') - Added DEFINITION link for array('class', 'method') structure ## Version 1.4.1 2018-08-20 - Tang Sanzang - Architecture Spined off Query for Gremlin, with Exakat DSL. - Centralized 'methods' property in Analysis class - Extended MAX_LOOPING usage - Analysis Added new thema : Class Review - Upgraded analysis : Defined Parent MP (less queries) - Upgraded analysis : Less false positives - Added support for PHP 7.2.9 - Checked unit tests : 2965 / 2980 test pass (99% pass). - Tokenizer Fixed Edge case with Ternary and Boolean - Added Staticpropertyname to distinguish from variables - Added support for remote definitions to methods - Removed global path for CIT (no fallback) ## Version 1.4.0 2018-08-13 - Sun Wu Kong - Architecture Chunked result inserts for Dump - More support for PHP 7.4 - Report Ambassador : added new Appinfo for relaxed Heredoc, trailing comma... - Analysis New analysis : class can be abstract - New analysis : trailing comma - New analysis : relaxed heredoc - New analysis : removed functions in PHP 7.3 - New analysis : continue versus break - Upgraded analysis : Hardcoded passwords is extended to objects - Checked unit tests : 2964 / 2979 test pass (99% pass). - Tokenizer Measure definitions stats for classes. - Added support for relaxed heredoc - Added support for closure as a return value - Refactored support for Ternary and Labels ## Version 1.3.9 2018-08-06 - Du Ruhui - Architecture Added support for PHP 7.4 - 'Copy' won't update anymore - Report Ambassador : fixed repeated 'compatibility' menu entry - Analysis New analysis : avoid __CLASS__ and get_called_class(). - New analysis : prepare for (real) deprecation - New analysis : const / define preference - New analysis : define case sensitivity preference - New analysis : avoid defining assert() in namespaces - Removed analysis : Variables/Arguments - Checked unit tests : 2957 / 2971 test pass (99% pass). - Tokenizer Removed Noscream - AT atom - Added definition for class constants - Fixed bug : can't apply ~ to false - Extended DEFINITION support to closure's use and references ## Version 1.3.8 2018-07-30 - Fang Xuanling - Architecture 'Copy' won't update code anymore. - Analysis Upgraded analysis : 'should use operator' only applies to constant chr() call - Upgraded analysis : Useless Instructions is faster - Checked unit tests : 2948 / 2962 test pass (99% pass). - Tokenizer Added support for variable definitions in methods ## Version 1.3.7 2018-07-16 - unnamed demon - Architecture Fixed handling of multiple updates - Report More documentations - Analysis New analysis : report usage of callback to process array - New analysis : report usage of case insensitive constants - Upgraded analysis : Hardcoded passwords is extended to objects - Upgraded analysis : Go To Key Directly handles comparisons - Added support for PHP 7.0.20 - Checked unit tests : 2948 / 2962 test pass (99% pass). ## Version 1.3.6 2018-07-16 - Zhang Gongjin - Architecture Added support for Rar archives - Removed call to gremlin server at 'status' time - Analysis New analysis : support for msgpack extension - New analysis : support for lzf extension - Upgraded analysis : added missing function names in several extensions - Checked unit tests : 2941 / 2955 test pass (99% pass). ## Version 1.3.5 2018-07-09 - Gao Shilian - Architecture Removed 4 unused exceptions - Extracted Query from Analysis - Report Reports : centralized all doc reading - Reports : doc reading now parses sections (avoid overlap) - Ambassador : Added exakat version and build to dashboard. - Ambassador : Added Class Tree (All class hierarchies) - Analysis Fixed bug with 'last' and '2last' - New analysis : Report undefined::class - New analysis : Report returned assignations as useless - New analysis : Split scalar typehint by versions - Upgraded analysis : Extended Reuse Variable to instantiations - Upgraded analysis : Masking parenthesis are only for referenced arguments - Upgraded analysis : Wrong case doesn't apply to parent/static/self - Upgraded analysis : Locally Unused Properties are extended to traits - Upgraded analysis : Should Preprocess is extended to concatenations - Upgraded analysis : Array_key_fill exclude variables by default - Upgraded analysis : Ambiguous static reports the whole property definition - Checked unit tests : 2919 / 2944 test pass (99% pass). - Tokenizer Added missing constants - Fixed support for goto true; - Fixed edge case for nested ternaries and boolean - Moved Goto and Label to Name Atom ## Version 1.3.4 2018-07-02 - Cheng Yaojin - Architecture Added check when unarchiving tar.gz and tar.bz - Added check for neo4j installation, (error grabing) - Moved Upgrade to tmp folder - Analysis Parameters are actually defined in the class - New analysis : ambiguous visibilities of properties - New analysis : report usage of PHP 7.1+ hash algorithm - New analysis : csprng (random_bytes and random_int) - New analysis : ext/libeio - New analysis : report incompatible signatures for methods - Upgraded analysis : Unused Private Methods handles fluent interfaces - Upgraded analysis : Defined Parent keyword - Upgraded analysis : Recursion - Refactored codeIs/codeIsNot - Checked unit tests : 2908 / 2923 test pass (99% pass). - Tokenizer Added support for 'parent' definitions - Fixed element counts in concatenation - Fixed operator priority in Strval - Upgraded handling of undefined constants to string ## Version 1.3.3 2018-06-25 - Ma Sanbao - Architecture Better handling of fallback to global for functions - Weekly code clean - Refactored several analysis for speed - Report Ambassador : fixed regression in the dashboard - Fixed edge case with properties - Analysis New analysis : closure that can be static - Upgraded analysis : empty function doesn't count static or global - Upgraded analysis : reported globals include $GLOBALS also - Checked unit tests : 2881 / 2911 test pass (98% pass). - Tokenizer Moved collection of functioncall to LoadFinal - Added collection of interfaces and newcall - Moved Declare to its own token - Moved Property definitions to its own token ## Version 1.3.2 coming up - Duan Zhixian - Architecture Reading stats from store, not graph. - Git now fails silently if login is requested at clone / pull - Report New analysis : == or === favorites - New analysis : > or < favorites - Upgraded analysis : written only variables is now faster - Upgraded analysis : PHP reserved words has now 2 parameters - Removed analysis : Type/Integer, Real, Closures. - Checked unit tests : 2901 / 2914 test pass (99% pass). - Tokenizer Static, PPP, Final and Abstract are now properties - Fixed regex in several rules - Added support for code clone detection (WIP) ## Version 1.3.1 2018-06-03 - Liu Hongji - Architecture Cleaned code of unused classes and ; - Fixed connexion script to the database - Fixed check of php.log folder - Report Ambassador : display correct compilation state - Analysis Upgraded analysis : used constant is also applied to defined() - Upgraded analysis : used protected methods is case insensitive - Upgraded analysis : Empty class omits extended classes - Upgraded analysis : More sequences to SimplePreg - Upgraded analysis : Throwable is not 'unthrown' anymore - Removed analysis : Static CPM - Checked unit tests : 2901 / 2914 test pass (99% pass). - Tokenizer Upgraded support for ::class ## Version 1.3.0 2018-06-03 - Xue Rengui - Architecture Added support for Tinkergraph 3.3.3 - Handles situations where exakat has no database - Check for PHP version at bootstrap - Report Ambassador : Updated PHP recommendation report with PHP 7.3 - All : Variables don't sport ... nor & anymore - Analysis New analysis : Single Use Variable - New analysis : Should Use Operator - New analysis : Check JSON production - New analysis : Report visibility usage with constants - Upgraded analysis : used constant is also applied to defined() - Upgraded analysis : used protected methods is case insensitive - Upgraded analysis : used directives handle function version - Upgraded analysis : added lcg_value for better rand - Upgraded analysis : Use Nullable extended to methods, closures. - Upgraded analysis : Fixed support for '_' native function - Checked unit tests : 2895 / 2907 test pass (99% pass). ## Version 1.2.9 2018-05-28 - Wang Gui - Architecture Removed query cache from gremlin - Added pre-query check to prevent queries that have no chance of result - Report Ambassador : first 50% of documentation fix : double quotes are not well displayed - Ambassador : Results are ordered by files, then by lines - Analysis New analysis : Flexible Heredoc syntax - New analysis : Non-compatible methods - New analysis : Use the Blind Var - New analysis : Inexistant Compact - New analysis : Typehint / default value mismatch - Upgraded analysis : strict_types are not recognized as undefined constant - Upgraded analysis : More new methods for PHP 7.3 - Upgraded analysis : Dependant traits - Upgraded analysis : Strpos comparison - Upgraded analysis : Method Must Return - Checked unit tests : 2885 / 2889 test pass (99% pass). - Tokenizer Interface may have const, not traits (Loading) - Added support for static call to methods ## Version 1.2.8 2018-05-21 - Xu Jingzong - Architecture Implemented a cache for speed boost. - Refactored files finding method - Git VCS always submit a user when cloning (using exakat by default) - Moved custom themes from themas.ini to themes.ini - Report Ambassador : fixed naming the audit - Ambassador : added 'Dead code' section - Doctor : split themes display (default/customs) - Analysis New analysis : Report what should be done in SQL - New analysis : Typehinted reference - New analysis : Strpos doing too much work - New analysis : Can't instantiate class - Upgraded analysis : Don't echo error - Upgraded analysis : PPP Declaration style - Upgraded analysis : Useless abstract class - Upgraded analysis : Buried assignation doesn't report declare anymore - Upgraded analysis : Abstract methods are not reported as unused - Upgraded analysis : relaxed version constraint for all Extensions/* - Checked unit tests : 2852 / 2856 test pass (99% pass). - Tokenizer Fixed handling of short_open_tags - Fixed edge case with % ## Version 1.2.7 2018-05-14 - Li Yuanji - Architecture Extended status command to all VCS - Added support for customized themes - Added Upgrading section, List of parametrized analysis, revamped summary - Simplified handling of commandline options - Removed usage of JSON for 'doctor' - Report A lot more documentation, examples, links. - Optimized type downloader - Added report themes pre-requisites - Analysis New analysis : ext/cmark - Upgraded analysis : too many children is configurable - Upgraded analysis : error_reporting 0 and -1 are not reported as issues. - Checked unit tests : 2835 / 2839 test pass (99% pass). - Tokenizer Fixed bug where constant self referenced. - Moved Identifiers to Names - Added first definitions for members. ## Version 1.2.6 2018-05-07 - Li Jiancheng - Architecture Moved more classes to helpers - Removed constants for Tokens - Upgraded to Robo 1.2.3 - Report Added support for custom themas for reports. - Analysis New analysis : zookeeper - New analysis : Report missing parenthesis - New analysis : Report invalid interval checks - New analysis : Suggest array_unique when possible - New analysis : Report when callback needs a return - New analysis : Reduce the number of if - Updated Exception list, up to PHP 7.3 - Upgraded analysis : Printf Arguments - Upgraded analysis : Count On Null - Upgraded analysis : Regex on Collector - Upgraded analysis : File Inclusion wrong case handles parenthesis - Upgraded analysis : Make globals a property - Upgraded analysis : Invalid regex - Checked unit tests : 2814 / 2818 test pass (99% pass). - Tokenizer Added definition links for staticmethodcalls. - Added boolean and int values to __DIR__ and co. - Removed several static properties - Fixed precedence of instanceof - Added support for Null val ## Version 1.2.5 2018-04-30 - Li Yuan - Architecture Added command 'config' to configure project from commandline - Made Exakat reentrant - Moved Configuration creation to external file - Upgraded status when audit isn't run yet - Analysis New analysis : Regex on Collector - Upgraded analysis : Only Variable with reference argument - Upgraded analysis : File Inclusion Wrong Case - Upgraded analysis : Invalid Regex - Added support for PHP 7.2.5, 7.1.17 and 7.0.30 - Checked unit tests : 2802 / 2809 test pass (99% pass). - Tokenizer Fixed various bugs with constant scalar expression ## Version 1.2.4 2018-04-23 - Li Chunfeng - Architecture Now fail with explicit message for memory running out - Report Ambassador : Updated 'confusing variables' report - Analysis Upgraded analysis : Could be short assignment - Upgraded analysis : Could be static - Upgraded analysis : Fail Substr Comparison (handles constants) - Checked unit tests : 2796 / 2801 test pass (99% pass). - Tokenizer Added propagation of constants when value can be processed - Introduced 'Parameter' token, to differentiate with Variable - Fixed syntax highlighting - Fixed a bug with negative bitshift ## Version 1.2.3 2018-04-16 - Yuan Tiangang - Architecture New append for logs - Report New report : Manual. - Ambassador : Rewrote the export of 'confusing variables' - Analysis New analysis : report strtr bad usage - New analysis : don't unset properties - Upgraded analysis : Invalid Regex - Upgraded analysis : Property Could Be Local - Upgraded analysis : No Hardcoded path - Upgraded analysis : echo/print preferences also report printf - Removed analysis : Close Naming (now done at Report level) - Checked unit tests : 2770 / 2786 test pass (99% pass). - Tokenizer Removed double definition for functioncalls ## Version 1.2.2 2018-04-09 - Yin Kaishan - Architecture Cleaned doctor so it works even without requirements - Fixed special chars with git URL - Report Ambassador : new inventory with classes changes in heritage - Ambassador : new inventory of large expressions - Upgraded report : Defined Exceptions are cleaned of doubles - Analysis New analysis : report Redefined Private Properties - New analysis : report substr() usage with strlen - Upgraded analysis for Inclusion Wrong Case filenames - Upgraded analysis : Cast To Boolean is extended to True/False - Upgraded analysis : Omit negative lengths - Upgraded analysis : interface search also include parameter counts - Upgraded analysis : Failed Substr Comparison handles special chars - Upgraded analysis : Identical consecutive omits arrays - Checked unit tests : 2757 / 2775 test pass (99% pass). ## Version 1.2.1 2018-04-02 - Fu Yi - Architecture Fixed generation of analysis logs - Fixed doctor, which wouldn't diagnostic the absence of needed extensions - Report More real-life examples in docs - Analysis New favorites : property declaration unique or multiples ? - New analysis : $a = +$b; - New analysis for Melis : Regex check and Route constraints - Upgraded analysis : Constant used below - Checked unit tests : 2760 / 2766 test pass (99% pass). - Tokenizer Fixed counts in property declarations - Fixed final new lines in heredoc/nowdoc ## Version 1.2.0 2018-03-26 - Xiao Yu - Architecture Upgraded concurrency with analysis - Replaced $_SERVER['_'] by PHP_BINARY - Removed old code (> 1.0.0) - Adopted 'stable' version for progressbar - Fixed loading with Bazaar - Added support for Parametrized analysis - Better initial configuration with doctor - Report Ambassador : upgraded analysis settings table - Analysis New analysis : Report Private functions for Wordpress - New analysis : Suggest simplifying chr(123); - New analysis : Too many native calls - Updated analysis : fallthrough are not reported with die - New Theme : Random - Collecting more stats for classes. - Checked unit tests : 2758 / 2741 test pass (99% pass). - Tokenizer Upgraded support for Heredoc ## Version 1.1.9 2018-03-19 - Qin Qiong - Architecture Better documentation for reports - Adding Real Code examples to documentation - Refactored Config reading - Moved more VCS information to its own class - Report Upgraded report : Ambassador reports the number of parameters in methods - New report : favorites (spin-off from Ambassador) - Upgraded report : Inventories also covers Dateformat, Regex, Sql, Url, Email, Unicode Blocks. - Analysis New analysis : too many parameters - New analysis : report mass creation of arrays - Checked unit tests : 2755 / 2738 test pass (99% pass). ## Version 1.1.8 2018-03-12 - Yuchi Gong - Architecture Reduced cache when running analysis - Fixed order of analysis - Report Ambassador : fixed faceted search problems - Codacy : added codacy-style report - Analysis New analysis : support for IBM db2, leveldb - New analysis : should use count's second argument - Upgraded analysis : Randomly sorted arrays - Checked unit tests : 2749 / 2731 test pass (99% pass). - Tokenizer Fixed edge case where die is an argument - Fixed edge case where Yield returns a array ## Version 1.1.7 2018-03-05 - Xu Maogong - Architecture Removed most static in Analysis - Report New format : All, that produces all reports - Ambassador : new report estimates fitting PHP version - Ambassador : report enable_dl in configuration - Analysis New analysis : report dynamic library loading - New analysis : suggest array_fill_keys() - New analysis : PHP 7.3 optional last argument - New analysis : added support for xxtea, opencensus, varnish, uopz - Upgraded BugFixes report to PHP 7.2.3 - Updated analysis : ext/cairo has new functions - Updated analysis : PHP 7.3 new functions - Removed analysis : NullCoalesce (double) - Checked unit tests : 2743 / 2731 test pass (99% pass). - Tokenizer Moved 'constant' to plugins - Fixed bug when updating with HG ## Version 1.1.6 2018-02-26 - Wei Zheng - Architecture Created 'First', a recipe of initial analysis - Prepared installation for compose - Report Restored 'INLINE' results - New reports : Stats - Collect PHP native function cool - Analysis New analysis : report suggest compact instead of array - New analysis : list with references (PHP 7.3+) - New analysis : report situation where check is done on non-cast value - New analysis : foreach( $array as $o -> $v) as error prone - Handle cases where PHP regex are not compilable anyway - Checked unit tests : 2732 / 2722 test pass (99% pass). - Tokenizer Propagate constant concatenation values. - Fixed calculation of intval - Refactored Configuration readers - Fixed bug when calculating __METHOD__ ## Version 1.1.5 2018-02-19 - Li Shimin - Architecture Refactored all reports - Removed outdated Devoops report - Report Upgraded BugFixes report to PHP 7.2.2 - Ambassador : generates a list of confusing variables - New report : OWASP - Analysis New analysis : Use Math - New analysis : Extensions ext/hrtime - New analysis : Possible Infinite Loops - Upgraded analysis : addZero, Multiply by one supports new situations - Upgraded analysis : added microtime, uniqid .. to better rand. - Checked unit tests : 2719 / 2724 test pass (99% pass). - Tokenizer Fixed check on script compilation that was too strict. - Fixed internal assert() - Exported VCS to separate classes - Refactored load with 3 separate plugins : intval, noDelimiter, booval ## Version 1.1.4 2018-02-12 - The Great White Turle - Architecture Build concatenation values in scalar constante expression. - Upgraded export of file dependencies values - Report Ambassador : fixed duration of audit. - Composer : provides a full list of depend extensions - Analysis New analysis : Report useless catch - New analysis : suggest using array_search / array_keys instead of foreach - New analysis : double array_flip is slow - New analysis : Suggest using cached values - New analysis : Functions that fallback to global namespace - Upgraded analysis : Encoded letters supports leading 0 in unicode codepoint - Upgraded analysis : Variable strange names now report 3 identical consecutive letters - Upgraded analysis : Upgraded support to __dir__ - Checked unit tests : 2716 / 2711 test pass (99% pass). - Tokenizer Fixed definitions link for functions ## Version 1.1.3 2018-02-05 - The fairy Su'e - Report Fixed Ambassador : the favorites weren't displayed. - Analysis New analysis : Report useless references - New analysis : Melis configuration : Undefined configuration array - New analysis : Melis configuration : make string. - Upgraded analysis : Parent first - Checked unit tests : 2700 / 2695 test pass (99% pass). - Tokenizer Better handling of Labels. - Fixed edge case where class and constants where mistaken one for the other ## Version 1.1.2 2018-01-29 - Jade Rabbit Spirit - Architecture Upgraded docs to tinkergraph 3.2.7 - Analysis New analysis : Report missing included files - New analysis : ZF3 : No Echo Outside a View. - New analysis : Local Global variable : report variable that looks global but are not - Upgraded analysis : Directive names are check with case sensitive analysis - Checked unit tests : 2687 / 2693 test pass (99% pass). - Tokenizer Magic Constant hold their actual value - Fixed Fullnspath for constants (case sensitive) - Fixed edge case with exit and die - Fixed edge case with exit and die and -1 ## Version 1.1.1 2018-01-22 - Wood Xie of Dipper - Architecture Fixed path when calling exakat from outside its install folder - First analysis for Melis Framework - Optimized dictionary collection - Report Ambassador : upgraded graph for class sizes - Analysis New analysis : report case problems with includes - New analysis : Melis framework - New analysis : inventory of view properties for Zend Framework - New analysis : report view files for Zend Framework - Upgraded analysis : + is accepted as regex delimiter - Upgraded analysis : same condition searches inside blocks - Checked unit tests : 2665 / 2671 test pass (99% pass). - Tokenizer Magic constants __DIR__ and __FILE__ get their actual value in noDelimiter - Created Eval atom - Removed 'Name' token for echo, print, die, exit. - Upgraded handling of constant names inside strings - Removed a bug when storing dictionary. ## Version 1.1.0 2018-01-15 - Wood Dragon of Horn - Architecture Replaced 'code' property with a dictionary - Tokenizer Introduced 'Magicmethod' for Magic methods in class - Fixed a bug when ' is in file path - Fixed a bug when several raw HTML are in a PHP script. ## Version 1.0.11 2018-01-08 - Wood Dragon of Well - Architecture Added assertion for property name. - Report Ambassador : Added report of classes's size. - Fixed missing audit end's time. - Analysis New analysis : Sqlite3 doesn't escape " - Upgraded analysis : Strange names also report qqqq sequences in variable names - Checked unit tests : 2617 / 2657 test pass (99% pass). - Tokenizer Fixed fullnspath handling for constants (case insensitive for the constant name) ## Version 1.0.10 2018-01-01 - Wood Wolf of Legs - Architecture Fixed Sqlite3 escaping error : use ', not " - Report - Analysis Upgraded analysis : ? is possible as delimiter - Analysis works better with nested structures - Checked unit tests : 2601 / 2649 test pass (99% pass). - Tokenizer First plugin for Load Task. - Upgraded support for define-d constant. - Introduced Phpvariable - Fixed scoping with array index. ## Version 1.0.9 2017-12-25 - King of Dust Protection - Report Ambassador : list complex expressions. - Dump : added function inventory - Dump : added begin and end line for structures. - Analysis New analysis : report reference error with Ternary operator - New analysis : report Undefined classes in Wordpress. - Upgraded analysis : preg option E, tighter regex. - Tokenizer Better handling of long path name. TBC. - Introduced Parent, Static, Self, Exit, Echo, Print. ## Version 1.0.8 2017-12-18 - King of Heat Protection - Architecture Doctor reports memory_limit and JAVA_OPTIONS/JAVA_HOME - Made database restart more portable - Added spell checking on docs - Report Ambassador : Regex inventory added - Ambassador : Largest expressions reported - Analysis New analysis : report identical operands on both sides of operator - New analysis : report potentially mistaken concatenation in array - New analysis : report mistaken scalar typehint - New analysis : report undefined classes by symfony version - New analysis : report undefined classes by wordpress version - Upgraded analysis : Interfaces are also reported from return typehint - Upgraded analysis : Mistaken concatenation got rid of various false-positives - Checked unit tests : 2601 / 2633 test pass (99% pass). - Tokenizer Isset, Empty, Phpvariables now have their own atom. - Fixed edge case with $ token - Fixed Constant fqn building - UTF-8 protection for propertyname ## Version 1.0.7 2017-12-11 - King of Heat Protection - Architecture Added /var to default omitted folders - Analysis New analysis : should use array_filter. - New analysis : ext/igbinary - Checked unit tests : 2533 / 2599 test pass (97% pass). - Tokenizer Fixed ## Version 1.0.6 2017-12-04 - Fuli - Architecture Refactored description - Moved PHPsyntax to a function - Analysis New analysis : Never used parameter. - New analysis : always use named boolean parameters - Upgraded analysis : unused arguments - Checked unit tests : 2573 / 2585 test pass (99% pass). - Tokenizer Added new token : This for $this - Updated loader to handle PHP 7.3 functioncall syntax (final ,) - Turned Markcallable into an independant analysis ## Version 1.0.5 2017-11-27 - King of Cold Protection - Architecture Configured Exakat for Tinkergraph 3.3. Still unfinished. - Documentation now has an external link to extensions. - Report Ambassador : added more inventories : URL SQL, email, GET index, MD5, Mime - Analysis New analysis : parent first - New analysis : Report uncommon Environment Vars - New analysis : Report invalid Regex - New analysis : Report contatenation in Zend DB - Fixed analysis : Deprecated Functions - Fixed analysis : Unknown PCRE2 option - Upgraded analysis : hardcoded password - Upgraded analysis : array_merge in loops - Upgraded analysis : substr() first. Handle following expressions - Refactored analysis : Used Functions - Refactored analysis : Add Zero - Checked unit tests : 2573 / 2585 test pass (99% pass). - Tokenizer Fixed a bug that linked functions and definitions ## Version 1.0.4 2017-11-20 - Boxiang Demon - Architecture PhpExec, get only path to binary. - Cleaned docs of double links - Cleaned code - Report Added libsodium, Argon2 to Crypto; DL() usage to PHP. - Compatibility report only focuses on backward incompatibilities. - New recipes will cover 'suggestions for better code'. Coming up. - Analysis New analysis : " string is better than ' (sorry...) - New analysis : PHP 7.3's PCRE 2 - New analysis : report missing 'new' in front of class name. - New analysis : use is_object instead of is_resource for ext/hash - New analysis : report non-countable calls - New analysis : report DL usage in Appinfo - New analysis : slice first, then map arrays. - New analysis : Avoid 5th argument in PHP 7.2 for set_error_handler - New analysis : avoid null with get_class() - New analysis : suggest using list() with foreach instead of arrays - New analysis : avoid using $this as argument in constructor - New analysis : Report usage of ext/vips - New inventory : GPC variables - Updated analysis : Use Class Operator doesn't report methods names anymore - Updated analysis : Long argument size is raised to 60 chars - Updated analysis : ignore when missing break is in last case - Updated analysis : Use This ignores 'self'. - Updated analysis : Randomly sorted Arrays ignores arrays of 3 or less. - Updated analysis : ext/mcrypt gets its constants - Updated analysis : more strange names being used in code - Updated analysis : more PHP 7.2 removed functions - Checked unit tests : 2563 / 2572 test pass (99% pass). - Tokenizer Reduced duplicated that may lead to loading error. ## Version 1.0.3 2017-11-13 - Baize Demon - Architecture Fixed driver Tinkergraph, which was not setting the right ids. - Doctor now reports $JAVA_OPTIONS, in case one need to allocate more memory - Doctor now reports token limit - Moved config.ini creation to first phase of init. - Fixed collect of error when init with git. - Upgraded driver gremlin-php to 3.0.2 - Report Ambassador : Now reports the namespaces as a tree. - New analysis : report members that are static and not. - Updated analyzis : normal method called statically. - Analysis Added support for Drupal, FuelPHP and Phalcon. ## Version 1.0.2 2017-11-06 - Suanni Demon - Architecture Better report of error messages from VCS. - Updated support for Vagrant - Report Ambassador : Fixed display for 'Callback' - Analysis New analysis : substr() first, then replace. - New analysis : report double prepare (WP). - New analysis : avoir the +1 month trap - New analysis : check for printf() options - New analysis : check for placeholder in prepare (WP) - New analysis : avoid direct injection into prepare (WP) - New analysis : performance recommendation for switch. - New analysis : merge if/if into if/then/else - Checked unit tests : 2500 / 2536 test pass (99% pass). ## Version 1.0.1 2017-10-30 - Xueshi Demon - Architecture Created Result class for Graphdb results - Docker image is updated with version 1.0.1 - Vagrant files are updated with version 1.0.1 - Preparing support for Gremlin 3.3.0 - Report Added support for PHP 7.1.11 and 7.0.25 - Analysis New analysis : could be else (for consecutive opposite if/then) - Checked unit tests : 2517 / 2527 test pass (99% pass). ## Version 1.0.0 2017-10-23 - Roushi Demon - Architecture Tested on Gremlin 3.2.6. Checked Gremlin 3.3.0, but it needs more work. - Upgraded doctor for installation and report. - Upgraded docs to set gremlin-server as default install. - Report Added support for Clang-style report. - Ambassador : fixed link to exception Tree. - Inventories : Date format, - Audit names are reported in every Ambassador-style report. - Analysis Upgraded PHP directive list. - Functions In For loop : prevent issue if the function uses a loop variable. - Useless instruction : do not report return $i++ if $i is reference - Useless instruction : Avoir reporting properties when they are magic - New analysis : mark properties to be magic. - Upgraded list of PHP logins, to report hard coded passwords. - Upgraded close naming : variables that differ with 1 chars are reported. - Added assert(false...) to list of branching syntax. - Checked unit tests : 2515 / 2525 test pass (99% pass). ## Version 0.12.16 2017-10-16 - Tawny Lion Demon - Report Beta version for Drill Instructor - Upgraded Inventories report with Sessions, Cookies, Incoming variables - Analysis New analysis : Expression too complex. - New analysis : Session Handler must implements SessionUpdateTimestampHandlerInterface - New analysis : is Zero : additions that negate some terms - New analysis : unconditional loops - Upgraded Zend Framework review with latest versions (feed, http, eventmanager...) - Upgraded 'Strange names' with new typos - Upgraded 'Logical to in_array' to handle separated comparisons - Checked unit tests : 2505 / 2515 test pass (99% pass). - Tokenizer Fixed bug with Sign in Additions. ## Version 0.12.15 2017-10-09 - Nine Headed Lion - Architecture Server : now supports stop, start and restart. - Every audit gets a random name, for easy differentiation - Added support for PHP 7.3 - Report Ambassador : list of analysis that report nothing : Good job! - Slim report : fixed build - Analysis New analysis : file upload names vulnerability check - New analysis : variable that may hold different types of date - New analysis : always anchor regex - Checked unit tests : 2475 / 2480 test pass (99% pass). ## Version 0.12.14 2017-10-02 - Grand Saint of Nine Spirits - Architecture Support UTF-8 on Gremlin Server (other encoding are not) - Better display of vcs updates - Report Ambassador : added Security and Performances - Ambassador : Upgraded exception presentation - Analysis New analysis : report fallthrough in switch - New analysis : inventory regex - Added support for PHP 7.1.10 and 7.0.24 ## Version 0.12.13 2017-09-25 - King of the Southern Hill - Architecture Code cleaning - Report Ambassador : changed display of the audit - Analysis Refactored several analysis ## Version 0.12.12 2017-09-18 - Ruler of the Kingdom of Miefa - Report Ambassador : fixed collect of interfaces and trait names - Analysis New analysis : ext/Parle - New analysis : help optimize pathinfo() usage - New analysis : catch array_values() usage with list and pathinfo() - Updated analysis : Don't show error messages with catch->getMessage(); - Updated analysis : No concat in loop handles $x = $c . $x; - Checked unit tests : 2456 / 2461 test pass (99% pass). - Tokenizer Added support for ', " and > in file names. Still missing support for \ - Restaured fallback to global constants. - Fixed special case : ## Version 0.12.11 2017-09-11 - Half-Guanyin - Architecture Added support options for branches and tags - Added support for config in server mode - Report Fixed methods dump for interfaces. - Analysis Added all analysis to report could be private/protected for - Tokenizer Fixed handling of '<' char in paths ## Version 0.12.10 2017-09-04 - Golden Nosed Albino Rat Spirit - Architecture Upgraded server version with config alteration features. - New generated config-cache - Report Fixed property names in Visibility report - Analysis Arrays/IsModified : arrays are not modified unless in a (unset) - Tokenizer Fixed 'constant' for functioncalls - Introduced 'Name' for Identifier without a fullnspath - Added support for branches and tags in init - Fixed edge case with $o->$$b ## Version 0.12.9 2017-08-28 - Lady Earth Flow - Architecture Creates config.cache, with cached calculated configs. Remove to update. - Report GraphQL : Upgraded GraphQL report, with relationships. - Analysis New analysis : suggest moving for() to foreach() - New analysis : shell_exec/exec/`backtick` favorite - Update analysis : Abstract Static is for PHP 7.0- - Tokenizer Removed Arguments and ARGUMENTS. - Finished 'factory' from Config. - Better handling of long path names. ## Version 0.12.8 2017-08-21 - ruler of the Kingdom of Biqiu - Analysis New analysis : use foreach, not for() - New analysis : ext/fam, ext/rdkafka - Tokenizer Fixed edge case where pathnames are too long on OSX. ## Version 0.12.7 2017-08-14 - Old Man of the South Pole - Architecture Fixed project_vcs when none is used. - Analysis Better documentation for in_array replacements and array_unique() - Added support for PHP 7.1.8 and 7.0.22 ## Version 0.12.6 2017-08-07 - White Faced Vixen Spirit - Analysis New analysis : no negative for strings before 7.1 - New analysis : use in_array instead of || - Updated analysis : preg_quote has no delimiter - Tokenizer Fixed bug in handling real value for negative numbers ## Version 0.12.5 2017-07-31 - White Deer Spirit - Architecture Removed config singleton - Report New report : simpletables (HTML) - Analysis New analysis : report optional parameters - New analysis : report concat inside a loop - Updated analysis : Could Be Class Constant, when no visibility is provided. ## Version 0.12.4 2017-07-24 - peacock Mahamayuri - Architecture Optimized performances for large projects (over 2M tokens) - Support Neo4j as a driver for Tinkgerpop - Report Now covering all PHP 7.2 features - Analysis New analysis : Extension xattr - New analysis : report 'object' as a class name - New analysis : No Array for magic property - New analysis : suggest reducing code for isset - New favorite : and / && - Updated analysis : fetch correct delimiter, even if escaped. - Extended coverage for several analysis - Removed several nested-subqueries (bad for performances) - Tokenizer Tinkergraph/Neo4j : reworked loading data from disk. - Added protection for $ in filename ## Version 0.12.3 2017-07-17 - Golden Winged Great Peng - Architecture Prepared options for several back servers : Tinkergraph, Gremlin-Server/Neo4j, Janusgraph - Report New report : Marmelab (GraphQL server) - Analysis New analysis : Report when a property is used as object or scalar - New analysis : Mismatched Typehint - New analysis : Mismatched Default values - Upgraded analysis : - Fixed a gremlin bug in noAtomInside - Tokenizer Added support for trailing comma in group use (PHP 7.2) - Fixed building of constants' values ## Version 0.12.2 2017-07-10 - Samantabhadra - Architecture Added support for Tinkergraph as graph backend - Report Ambassador : reports callback/closures, all 3 declares (ticks, encoding, strict_types) - Ambassador : reports strict_types as favorite - PlantUML : upgraded report - Analysis New analysis : Mismatched ternary branches - New analysis : mkdir, by default, uses 777. - New analysis : ext/lapack - Upgraded analysis : option E for preg_match, refined results - Checked unit tests : 2337 / 2366 test pass (99% pass). - Tokenizer Added support for Instanceof and GROUPUSE with Nsname ## Version 0.12.1 2017-07-03 - Yellow Toothed Elephant - Architecture Refactored structures extractions in dump - Report New report : PlantUML - Ambassador : Appinfo now reports how popular is a feature - Analysis New analysis : Const / Define() favorite for constants - New analysis : do not return in finally - Upgraded analysis : Add Zero was refactored - Tokenizer Prepared list of tokens and relations ## Version 0.12.0 2017-06-26 - Manjusri - Architecture Added support for Janusgraph (Gremlin 3) - Refactored dump's data collection for speed.bb - Report Added support for Wordpress and Joomla as Frameworks - Analysis New analysis : Avoid Optional properties - New analysis : Multiple declarations of functions - New analysis : Non breakable spaces in names - New analysis : Favorite Heredoc delimiter - New analysis : ext/swoole - Tokenizer Modified several nodes/links names, for compatibility purposes ## Version 0.11.8 2017-06-19 - Xiaozuanfeng - Architecture Starte working on JanusGraph to add to Neo4j/Gremlin3 - Report Ambassador : reports Strings encoding and Unicode-block (when available) - Ambassador : reports framework founds (first 6, more as we go). - Ambassador : reports how frequently an analysis yield results to compare with current situation - Analysis New analysis : Classes where declaration order differs from : use, const, properties and methods. - New analysis : Could use interface (but implements is missing) - New analysis : Cant Inherit Abstract Method (PHP 7.2 upgrade) - New analysis : use session_start() options - Updated analysis : Dynamica method calls cover {} too - Checked unit tests : 2305 / 2305 test pass (100% pass). - Tokenizer Checked code on early PHP 7.2 version ## Version 0.11.7 2017-06-12 - Long Armed Ape Monkey - Report Ambassador : report detected patterns (2 firsts) - None report : for when dump is sufficient - Analysis New analysis : could factor functioncalls - New analysis : PSR-* usage - New analysis : support for Judy and Gender extensions - Added thema for Compatibility PHP 7.3 - Added thema for Dependency Injection - Tokenizer Fixed edge case where classes starting with 'namespace' where mistakenly processed - Removed Block from CIT ## Version 0.11.6 2017-06-05 - Red Bottomed Horse Monkey - Architecture Removed singleton to Config. WIP - Report Ambassador : reports usage of PSR 3,6,7,11,13,16. - UML : report now protects file names - Analysis New analysis : Ext stats - New analysis : report mixed concatenation / interpolation strings - Updated analysis : htmlentities actually uses combinaison, not alternatives, - Updated analysis : Close Tag consistency ignores __HALT_COMPILER files ## Version 0.11.5 2017-05-30 - Intelligent Stone Monkey - Report Ambassador : fixed visibility suggestion - New report : Dependency wheel - Analysis New analysis : avoid typehinting with classes - New analysis : implemented methods must be public - New analysis : no reference on left of assignement - New analysis : Could typehint with instanceof - Updated analysis : Useless parenthesis cover clone, yield, yield from. - Updated analysis : Make One Call also reports nested calls - Tokenizer Split functions and closures, - Split classes and anonymous classes - Split variable with definitions (Property, Static and Global) - File count is always reported (even 0) ## Version 0.11.4 2017-05-22 - Six Eared Macaque - Architecture Results : returns now multiple results at once - Report New report : codeflower - Ambassador : report usage of Debug functions, browscap - Ambassador : omits 0 in donuts - Ambassador : faceted search for compatiblity - Analysis New analysis : report functions whose return is not used - New analysis : only variable can be passed by reference - Added limits to all in-depth searches - Checked unit tests : 2216 / 2216 test pass (100% pass). - Tokenizer Fixed edge case, where return is finished by a close tag - Split Variables into Variables, Objects and Arrays. ## Version 0.11.3 2017-05-15 - Sun Deity of Mao - Architecture Speed up batch processing for lists of analysis - Split data collection from the initial dump. - Report Ambassador : Upgraded presentation of issues, and internals links. - Analysis New analysis : Sphinx extension - New analysis : GRPC extension - New analysis : reports arrays that are randomly sorted. - New analysis : report multiple catch clauses - Updated analysis : direct injections include all SERVER_* values - Upgrade for PHP 7.1.15 and 7.0.19 - Tokenizer Split Functioncall into Functioncall, MethocallCall and Newcall. - Added support for 'namespace' in any full name. ## Version 0.11.2 2017-05-08 - Scorpion Demon - Architecture Code cleaning, and more stability - Analysis New analysis : Report preference between != and <> - New analysis : report empty regex and wrong delimiters - Added protection for $ in RegexDelimiters ## Version 0.11.1 2017-05-01 - Ruler of Women's Country - Architecture Fixed handling for large list of data in gremlin queries - Handles static in anonymous classes correctly - Report Reports handle traits like class. - Analysis New analysis : ends arrays with , or not (favorite) - New analysis : suspicious comparison - New analysis : strange spaces in strings - Tokenizer Arrays are now Arrayliteral, split from Functioncall ## Version 0.11.0 2017-04-24 - Immortal Ruyi - Architecture Removed prepared statements from loops in dump - made Gremlin cache compatible with 32bits platforms - Report Ambassador : first work on upgrading visibilities for properties. - Analysis New analysis : could use str_repeat() - New analysis : Crc32() Might Be Negative - Update analysis : Queries in loop reports cubrid and sqlsrv, prepared statements. - Update analysis : type mismatch for indices works on constants too. - Update analysis : Loop calling covers less ground - Tokenizer Split function and method entities for differentiated processing ## Version 0.10.9 2017-04-17 - Single Horned Rhinoceros King - Architecture File extensions are processed before include/ignore dirs. - Reduced number of DEFINITION links, leading to less processing. - Added several assertion() in the code - Added assertions report in doctor (better leave them out with phar) - Report Added support for PHP 7.0.18 and 7.1.4 - Ambassador : better layout for favorites - Zend Framework : 8 new components supported - Zend Framework : now supports zendframework/zendframework too - Zend Framework : report unused components - Analysis New analysis : report nested Use expressions - New analysis : report repeated regex (to be federated) - New analysis : report code that output directly to std - Updated analysis : Should use this now omits overwritten methods - New analysis : report overwritten methods - Upgraded analysis : 2123 / 2123 test pass (100% pass) ## Version 0.10.8 2017-04-10 - King of Spiritual Touch - Report Slim report : list of routes used. - Analysis New analysis : report Group Use Declaration (PHP 7.0+) - Zend Framework : 30 components are now covered. - Slim : No echo in route callable and Inventory of routes. - PHP : list of new PHP 7.2 functions. - Tokenizer Sped up loading time by 10%. - Added support for PHP6 binary string : $a = u'b'; ## Version 0.10.7 2017-04-03 - Immortal of Antelope Power - Report Ambassador : fixed composer report. - Added report for Composer (beta phase) - Added report for Slim framework. - Analysis Added support for Slim versions. - Added 10 new components for Zend Framework 3 - Tokenizer Fixed support for $ in file names. ## Version 0.10.6 2017-03-27 - Immortal of Elk Power - Architecture Major speed up of loading and analysis - Fixed themes configuration. - Report Ambassador : report cookies usage, infinite and NAN usage - Zend Framework : Report incompatibilites component/version for ZF3 - Analysis Upgraded analysis : 1941 / 1941 test pass (100.00% pass) - New analysis : Zend Framework 3 Deprecated - New analysis : Zend cache, view, db. - New analysis : Report missing type tests. - New analysis : suggest setcookie() with safe arguments - New analysis : Do not cast to Int - New analysis : CakePHP classes compatibilities from 2.5 to 3.3 - Upgraded analysis : instanceof doesn't report traits anymore - Upgraded analysis : mb_ereg has options in the 4th arguments - Upgraded analysis : more strange names - Tokenizer Reviewed most of the load processing. - Reduced the number of 'fullnspath' properties. ## Version 0.10.5 2017-03-13 - Immortal of Tiger Power - Architecture Collect graph size in dump.sqlite - Collect memory usage in dump.sqlite - Now uses the calling PHP version to run all parts of exakat (no config) - Doctor report the ran gremlin version. - Report Ported the Zend Framework report to ambassador - Added regex delimiter in favorites. - Ambassador : syntax coloring - Analysis New analysis : could be typehinted 'callable' - New analysis : encoded letters in strings for security - New analysis : report arguments that may be callable - New analysis : report strangely named variables - New analysis : report strangely named constants - New analysis : too many FindsBy*() methods - Updated analysis : Useless Instructions doesn't report array_merge(_recursive) with one argument - Updated analysis : array_replace handles ... - Updated analysis : 7.2 deprecation with assert() - Generalized usage of commons for CIT - Added first 4 set of analysis for Zend Framework 3 - Added support for dynamic new $a[i]; - Tokenizer Fixed fullnspath with new on functioncall - Reduced the number of fullnspath loaded - Added support for 's'() as functioncall - Fixed case where file names has ' ' in it ## Version 0.10.4 2017-03-06 - Dragon King of the West Sea - Architecture Ignore some classic files by default (README, LICENSE...) - Report Ambassador : protection of HTML values - PHPcompilation : fixed export to stdout - Analysis New analysis : report useless else branches - New analysis : should regenerate session Id, for PHP and Zend Framework - Added support for Extension Data structures (ext/ds) - Upgraded analysis : Hardcoded Hash - Speed up analysis for extensions - Tokenizer Fixed edge case where a constant was used inside a ternary operator - Fixed processing of labels ## Version 0.10.3 2017-02-27 - Dragon King of the Jing River - Architecture Added URL glossary to Manual. - Extended CS ruleset - Use exakat/exakat as user/login for git. - New helper to rename analysis - Project command now accept -P/-T to run one analysis/Thema directly - Report New report style : Codesniffer - Analysis New analysis : suggest usage for array_column() - New analysis : __DIR__ must be concatenated with a string starting with '/' - New analysis : report usage of parent, self and static outside a class/trait - New analysis : report properties used only in one method - New analysis : report properties used only once at all - New analysis : multiple aliases per class - Updated analysis : Fopen() mode support 'e' option (7.1.2 + ) - Updated analysis : Make One Call covers str_replace, substr_replace, preg_replace* - Updated analysis : Unused arguments : now ignores arguments from interface or parent - Tokenizer Removed double DEFINITION link. Faster loading, less processing. - Fixed an edge case when function name is boolean or null. - Cleaned atom and tokens names - Fixed edge case when object is instantiated in a ternary ## Version 0.10.2 2017-02-20 - Water Lizard Dragon - Architecture - Report Text format now understand -T, -P to extract only some of the results. - Fixed dump of extends. - Analysis Added support for PHP 7.1.2 and PHP 7.0.16 - New analysis : report forgotten 'throw' keyword. - New analysis : report class / function confusing name - Added support for libsodium - Upgraded PHP Relaxed Keyword : Ignore properties. - Upgraded analysis : 1824 / 1826 test pass (99.9% pass) - Tokenizer Fixed a bug that mistakes native PHP classes for functions - Fixed rare situation with grouped const/function. ## Version 0.10.1 2017-02-13 - King of Wuji Kingdom - Architecture Report SVN revision when updating or not. - Default reports are in config. - Configure now supports include_dirs, to include files. - Project name is now noted in datastore. - Inventories is a default themas; PHP Compatibility < 5.6 are not default anymore. - Documentation Fixed outgoing links - Better coverage of PHP functions - Report Added 'Inventories' report : reports all names and literals - Ambassador : Added list of included files, Yield From and classes stats - Analysis New Analysis : Strange Names For Methods (Classes/StrangeName) - New Analysis : SQL queries (Type/Sql) - New Analysis : Avoid Non Wordpress Globals (Wordpress/AvoidOtherGlobals) - Upgraded analysis : Should be single quote, escape sequences refined. - Upgraded analysis : Should Preprocess now support determinist PHP functions - Upgraded analysis : 1817 / 1824 test pass (99.6% pass) - Tokenizer Fixed LOC counting. - Fixed edge case when closure is directly use as argument - Fixed double inventories for Use's Definitions ## Version 0.10.0 2017-02-06 - Azure Lion - Architecture Replacement of booleans with constants (WIP) - Removed PHPloc (merged features into load) - Added coding standard for Code Sniffer (ruleset.xml) - PHP version used default to running script version - Now reading Token Constants from the binaries - Doctor reports project configuration if -p is used - Report - Analysis New Analysis : No Boolean As Default - New Analysis : Raised Access Level - New Analysis : Recommend Wpdb->prepare when variables are in query - Directive suggestion now include error_log - Upgraded analysis : UselessParenthesis also checks Typehint - Upgraded analysis : 1804 / 1811 test pass (99.6% pass) - Tokenizer Reinforced detection of parsable PHP script - Fixed Files command : it now cleans data before running - Removed warning about memory - Index creation made lighter ## Version 0.9.9 2017/01/30 - Pilanpo Bodhisattva - Architecture Moving true/false to constants - Report Ambassador : Added 'Compilation' and Version compatibility reports. - Prepared collection of dependencies in dump - Analysis New Thema : Compatibility PHP 7.2 - New analysis : Deprecated Features of PHP 7.2 - New analysis : Removed Function for PHP 7.2 - New preference : New Line Style - Upgraded analysis : 1781 / 1802 test pass (98.9% pass) ## Version 0.9.8 2017-01-23 - Multiple Eyed Creature - Architecture Moved 'Truthy/Falsy' as 'boolean' characteristics - Updated Gremlin3 interface to handle Groovy maps - Added default name when creating project - Report Added checks on merged table at Dump stage - Added support for PHP 7.1.1 and 7.0.15 - Analysis New analysis : variables assigned twice or more - New preference : new x() / new x; - Upgraded analysis : 1785 / 1794 test pass (99.5% pass) - Fixed Interface usage : missing interfaces extends interfaces - Added extra check for Functioncalls - Tokenizer Added support for instanceof + several names ## Version 0.9.7 2017-01-16 - Hundred Eyed Demon Lord - Architecture Fixed constant names for tokens in Load - Changed duplication check to dedup(). Cleaned analysis for duplicates. - Speed but for large projects. Work in Progress. - Reduced usage of static properties - Better detection of PHP scripts during project - Report Fixed generation of inventories when no target is provided - Analysis New analysis : Could Be Protected Property (not a public) - New analysis : avoid large literal arrays in local variables. - New analysis : report long arguments. - Removed analysis : Structures/EchoArguments (double with Echo With Concat) - Tokenizer Fixed list of constants for PHP 7.1 ## Version 0.9.6 2017-01-09 - Spider Demons - Architecture Added support for report/analysis theme list in config (exakat and project) - Better cleaning of projects - Doctor : Initialisation with themes/reports; Reports executable being used. - Added a log for gremlin Queries - Rebuild the server command - Added 'catalog' command - Report Split Phpconfiguration into eponymous and Phpcompilation - Analysis New analysis : avoid Glob, use scandir without sorting. - New analysis : always configure ext/sqlite3 FetchRow() - New analysis : no string with append - Removed analysis : Structures/ForeachSourcesNotVariable - Upgraded Analysis 'Should Import Functions' - Upgraded analysis : 1764 / 1773 test pass (99.5% pass). - Tokenizer Added 'aliased' property to nodes. ## Version 0.9.5 2017-01-04 - Immortal Ziyang - Architecture Better check of PHP version - Report Ambassador : report analysis settings - PHP Compilations : supports all extensions - New report : Inventories - Analysis New analysis : Don't Use Fallback to Global space - New analysis : MongoDB (ext/mongo version 3) - New analysis : zbarcode - Bug : Fixed intval for octals in Arrays/MultipleIdenticalKeys - Removed analysis : Php/InconsistantClosingTag (double) - Tokenizer Ranking arguments, not functioncall ## Version 0.9.4 2016-12-19 - Lady of Jinsheng Palace - Architecture Rewrote the concurrence check (removed needs for ext/sem) - Results are never double anymore - Upgraded gremlin calls, to handle \n - Dump cleans the previous values before dumping - Excluded namespaces classes when searching for external libraries - Report Ambassador : extension usage, inventories, global lists, stats, PHP Compilation directives - Covers more compilation directives (Not finished) - Analysis New analysis : Final by Ocramius - Upgraded : Comparison with == : added curl_exec - Upgraded : isset with constant (mistake on properties as arrays) - Upgraded : Avoid using now uses full NS path - Upgraded : Useless instructions handles for() correctly - Upgraded : Recursive, IsGenerator and Loop Calling includes yield from - Upgraded analysis : 1741 / 1750 test pass (99.5% pass). ## Version 0.9.3 2016-12-12 - Purple-Gold Bells - Architecture Lots of cleaned code - Harmonized data for extensions - Stop 'project' if no code is available - Now using stub in phar. - Report Added directives, bugfixes, external services and - Added support for PHP 7.0.14 and 5.6.29 - Analysis New analysis : Wordpress, recommend prepare() - More favorite reports : final ?> and unset()/(unset) - Reduced number of double reports for many analysis - Update : Fixed analysis with $THIS - Upgrade : report useless casting of comparisons - Update : Should use this takes into account parent ## Version 0.9.2 2016-12-05 - Golden Haired Hou - Architecture First version of Exakat for docker (beta) - Added a waiting loop in cleandb - Docs include a list of new analysis per version - Report Added 2 first inventories, Appinfo() in Ambassador - Favorites now reports global/$GLOBALS - Restore composer.lock report - Upgraded uselessReturn for the final return. - Analysis New analysis for Newt, Nsapi, - New analysis : __ in methods names - New analysis : Too many local variables - New analysis : Avoid array_push() - Upgraded ext/apache coverage ## Version 0.9.1 2016-11-28 - Sai Tai Sui - Architecture Docker supported in exakat/config.ini for PHP binaries. - Added exakatSince in analysis documentation - Added some missing tokens in anonymize command - Report Added several new analysis for PHP 7.1 - Analysis new analysis : find methods that could return Void - new analysis : find malformed octal sequence in strings - new analysis : spot rethrown exception - new analysis : reach the last element - new analysis : find undefined Zend Framework classes (2.0 to 3.0) - Upgraded analysis : 1706 / 1714 test pass (99.5% pass). - Tokenizer Fixed handling references (some were missing) - Fixed handling of ellipsis (some were missing) ## Version 0.9.0 2016-11-21 - Python Demon - Architecture Project now include 'Preference' analysis - Dump is now incremental (-u option), and doesn't need to be run in paralell - Added new hashAnalysis table, to handle generic results from analysis. - Added project name in the graph. - New command 'status' to report the current status of exakat - Report Ambassador includes 'Preferences' section and new menu system - Upgraded progressbar to display project processing - Analysis New analysis : Early Bail Out (with if/then) - New analysis : PHP 7.1 backward incompatibilities with microseconds - New analysis : Wordpress : recommend using WP api, not PHP. - Upgraded 'Constant condition' to include do..while() - Upgraded 'Useless Abstract' to include methodless classes - Upgraded analysis : 1687 / 1697 test pass (99% pass). - Tokenizer Added 'Array' to list of determinist functions (more constants are spotted) - Fixed 'Name' for Array Short Syntax. - Fixed variadic support ## Version 0.8.9 2016-11-14 - Yellow Brows Great King - Architecture Fixed and document -tgz and -zip option of init - Removed progress folder - Made MagicNumber a parallel task in Project. - Turned some die into assertion() - .phar doesn't report any PHP errors. - Checked compilation with PHP 5.3->7.2 - Report Removed Faceted report - Added Bugfixes for PHP 7.0.13, 5.6.28 and PHP 7.2 - Added 'One variable string' to Radwell report - Analysis New analysis : Object Calisthenics #1, #4 - New analysis : check that properties are all set at constructor time. - New analysis : spot useless checks - Updated UndefinedParentMP to take PHP ext classes into account - Upgraded 'array_merge in loops' with file_put_contents - Upgraded 'useless parenthesis' with math operations - Upgraded analysis : 1666 / 1682 test pass (99% pass). - Added debug Query method to analysis - Tokenizer Fixed Files to compile first, then count tokens - Find Ext Lib handle UT classes better - Added limit to 'code' before loading into database. There is a 2M limit. - Fixed edge case with nested foreach() - Fixed segmentation fault when getting tokens from a script with wrong encoding ## Version 0.8.8 2016-11-07 - Apricot Immortal - Architecture Added concurency test to avoid running several instance at the same time - Report error when it happens with git clone - Added UT classes to external libraries - Dump is now hidden until finished. - Better detection of java and composer (Thanks Julien) - Report New report : Radwell - New report : PhpConfiguration helping with configure and php.ini - Ambassador : Fixed dashboard values - Analysis New analysis : time() vs strtotime('now') - New analysis : useless casting - New analysis : No Isset() with Empty() - New analysis : don't echo errors - New analysis : ext/rar - New analysis : use Class::class when possible - Added array_key_exists() to slow functions list. - Upgraded UpperCaseKeywords to handle partial uppercase - Added reported directives for ext/filter - Upgraded 'Variables used once' to exclude $this and arguments - Upgraded Unreachable Code with break/continue; - Multiple Identical Keys now handles null, boolean, real. - Upgraded analysis : 1652 / 1668 test pass (99% pass). - Tokenizer Now spots \true, \false, \null as Boolean and Null - Removed 'xargs too many arguments' error on Linux ## Version 0.8.7 2016-10-31 - Naked Demon - Architecture Upgraded Boolean and Integer to report results without storing them in graph - Analysis New analysis : modernizable empty() calls - New analysis : recommend Positive conditions - New analysis : drop else after return - Upgraded analysis : unreacheable code handles if/then with returns. - Added tests for Boolean and Null - More not Hashes dict. - Upgraded analysis : 1637 / 1650 test pass (99% pass). - Tokenizer Fixed line number of - Fixed path for reporting ## Version 0.8.3 2016-10-03 - Guzhi Gong - Architecture Created temp folder .exakat in projects_dir - Removed mentions of float, only using Real - Moved Config to Exakat\Config - More examples in docs - Report Added settings and files to Ambassador - Analysis New analysis for dependant Traits - Added new Theme 'Cakephp' with 6 analysis for migration - New values for Not-a-hash - Unresolved Catch now takes Throwable into account - Tokenizer Fixed edge case where return is used inside if/then without {} nor value. - Fixed 'code' and 'token' for ?: and () ## Version 0.8.2 2016-09-26 - Jinjie Shiba Gong - Architecture More examples in docs - Fixed 'file' in results - Report Added more media for Ambassador - Analysis New analysis for count/strlen compared to 0 - Upgraded analysis : 1563 / 1579 test pass (99% pass). - Backported all 4 Wordpress analysis (wpdb, nonce usage) - Added new Wordpress analysis : variable escaping in templates - Tokenizer Fixed = 0) - Added analysis for PHP 7.1 : removed directives, added functions - Upgraded analysis : 1485 / 1522 test pass (97.5% pass). - Tokenizer Fixed edge case with value - Better support for PHP 7 indirect expression - More directives for xdebug - Eval Without Try is PHP 7 only - No Choice analysis is now case insensitive - Tokenizer Added support for keys in list() (PHP 7.1) - Added support for constant visibility (PHP 7.2) - Added support for Multi catch : catch(A|B $e) (PHP 7.1) - Fixed bug with + and instanceof - Fixed precedence between :: and ?? ## Version 0.6.4 2016-05-09 - Bull Demon King - Architecture Externalized the list of recognized libraries to Json - Added 'Wordpress' and 'Coding convention' as Recipes - Report Initial report for Zend Framework. Still prototyping. - Analysis Accelerated analysis for Implicit GLobals variables - New analyze : Indirect Injections (Security) - New analyze : Should Use Coalesce (code upgrade) - New analyze : Suggest dirname(__FILE__) => __DIR__ - Added 'str_rot13' as unsafe 'crypto' - Properties without default can't be redefined - Added Yield and Yield From as structures without parenthesis needs - Double Assignation, unless 2nd call is a functioncall (less false positives) ## Version 0.6.3 2016-05-02 - Jade Faced Princess - Architecture Removed several useless pieces of code (self analysis) - Added documentation for Wordpress Recipes - Lengthened Cycle for tokenizer - Report Added bugfixes for PHP 7.0.6, 5.6.21, 5.5.35. - Now reporting token counts per files - Analysis New analysis : Spot variable that holds $_GET, $_POST, $_REQUEST or $_COOKIE values (internal) - New analysis : Report variables that are overwritten by themselves - New analysis : Report useless switch (empty, 1 case only) - Upgraded NoChoice to handle larger sequences - Upgraded Useless Global to handle global $x / $GLOBALS['x'] situations - New analysis : Wordpress Recipe : Unverified Nonce, Best Usage for $wpdb - New analysis : Void for PHP 7.1 - Tokenizer Fixed but with Typehint - Added phppowerpoint class in external libraries ## Version 0.6.2 2016-04-25 - Long Armed Ape Monkey - Architecture Fixed phar detection (based on ext/phar) - Cleaned code with myself - Report New report format : clustergrammer - Analysis New analysis : same conditions in If / Then - New analysis : spot dead code in catch expressions - Static loops now exclude methods usage - Indirect variable expression are stricter - preg_* Option e has better support for delimiters - Upgraded Direct Injection in case of concatenation - Detect Ellipsis when counting arguments - Could use short assignation : avoid $a += $a + 3; - Tokenizer Sped up Typehint detection - No indexing for T_STRING in properties - Reduced errors from token_get_all() ## Version 0.6.1 2016-04-18 - Red Bottomed Horse Monkey - Architecture Prepared to support PHP 7.1 - Fixed bug in user / passwords when initing the project - Better support for ::class when searching for libraries - Analysis UselessParenthesis : spot nested parenthesis - Spot exceptions that are thrown but uncaught by the current code - Support for ext/lua, - New : Check catch order in try/catch - Better identification of Composer classes, based on composer.json - Now spot interfaces in use declarations (less undefined interfaces) - Tokenizer Added support for PHP 7.1 - key => value in list() calls - visibility for constants in Classes and Interfaces - Accelerated up Typehint support ## Version 0.6.0 2016-04-11 - Intelligent Stone Monkey - Architecture Fixed a bug in Find external libraries - Applied fixed based on new analysis audit - Fixed a bug that prevented results to be prepared for report (Thanks Philippe G.) - Report Now reports reason for excluding a file from analysis - Analysis New analysis : Logical Mistake (first version), - New analysis : Iffectations (code restoration) - New analysis : Common alternatives - New analysis : No Choice (No alternatives) - New analysis : Random_* Without Try (security risk) - New analysis : Unknown PCRE options - New analysis : Identical conditions - New analysis : Hardcoded hashes - Upgrade List with appends with variable name - Upgrade /e option detection - Fixed detection of unused use, with long namespaces. - Added finfo to ext/finfo - Finds exceptions that are reserved for later throwing - Exclude anonymous classes from Already Defined Interface - Tokenizer Extended cycle number to speed up tokenizer. - Better escaping of file names ## Version 0.5.9 2016-04-04 - Six Eared Macaque - Architecture One progressbar per Recipe during project analysis - report's documentation - Upgraded 'External Lib' to ignore Composer folders. - Fixed a bug about interpreting tokens - Dump collects classes, interfaces, traits definitions - Now storing project name in database for future use - Removed PHP configuration modifications (error_reporting, display_errors) - Report Added 'Uml' report : hierarchy report - Now reports Pear Usage - Upgraded Bugfix database for 7.0.5, 5.6.20 and 5.5.34 - Report Yield (from) usage - New external configuration files : bazar, github, docker, openshift - Analysis Added detection for undefined classes in ZF (1.8 to 1.12) - New : report undefined Traits - Added support for parent/grandparent when checking argument numbers - Added support for V8js - Tokenizer Fixed bug in fullnspath for use within trait or class - It is possible to reach a property on an array append - Fixed AST between PHP 5 and 7 for globals - Simplified ++ analysis ## Version 0.5.8 2016-03-28 - Sun Deity of Mao - Architecture Moved to self::, instead of static::. - First UT for command line - Sped up phploc. Prepare code for finite states, in Tasks. - Prepare for Gremlin3 (moved gremlin calls to class) - Reduced shell_exec usage - Report Fixed display bugs in Devoops report - Removed double analysis - 'Wrong number of arguments' now supports constructors - Analysis Upgraded 'No Hardcoded IP' to handle constants, spot domains - Added support for TokyoTyrant - New analysis : spot simple regex, and suggest strpos - Excluded "$a[b]" from undefined constants - Tokenizer Fixed bug with nested call to echo. - Fixed bug where concatenation ends on a 'AS' keyword - Added support of Constants in Foreach - Fixed multiple bugs in Grouped Use - Support for function as 'class' in static calls - Comparison accepts powers - Added support for empty array short syntax in sequence - Support constant with visibility - Parenthesis may be the base for Arrays ## Version 0.5.7 2016-03-21 - Scorpion Demon - Architecture Added support for folders in UT, for tests that requires several files - Improved compatibility with PHPunit - Moving gremlin_query() to Gremlin2 class - Doctor also reports for phar - Improved adaptation to PHP and Exakt in server mode - Autoload shouldn't die - Fixed case when calling Phpexec - Upgraded status presentation in server mode - Report More details for Global Variable list - Analysis Now spotting class when it is inside a string - Check for $this outside a trait/class - Check for ternary/concatenation precedence - Spot classes that attempt to extend final - Spot set_exception_handler() that may need rework - Refined array_merge analysis, in case of nested loops - Tokenizer Yield [from] may be inside an array - Refactored for/foreach tokens - Added support for a 'Project' node ## Version 0.5.6 2016-03-14 - Ruler of Women's Country - Architecture Fixed some backward compatibility with PHP 5.4 - Started revamping 'Status' command - Centralized all tokenizations to PhpExec class - Removed usage of __DIR__ and __FILE__ - Analysis Spot usage of empty() that can't work on PHP 5.4 - Suggest using random_int instead of rand - Upgraded 'No Array_merge in loops' with array_merge_recursive - Added support for scalar type hint in Undefined Classes - New analysis : Better rand() - Tokenizer Instanceof has lower precedence than comparison ## Version 0.5.5 2016-03-07 - Immortal Ruyi - Architecture Added default values for all neo4j_* configs - Report Added support for bugfixes in 7.0.4, 5.6.19 and 5.5.33 - Added support for bugfixes in 7.1.0-dev - Analysis Added support for Typehint in Undeclared Classes - Extended 'Multiple Classes in One File' to interfaces and traits - Added analysis for truthy and falsy - Spot interfaces implemented by parents (Thanks PHP Inspect) - Report usage for unsafe Curl options - Tokenizer Fixed emptyString inside a Heredoc - Fixed bug where Sign has lower priority than Power ## Version 0.5.4 2016-02-29 - Nezha - Architecture Removed some shell_exec() to help with portability - Clean command now rebuilds an empty datastore - Check the availability of php binaries before using - Produce report in a hidden folder, then push it - Report Report the list of bug fixes that apply to code - Analysis Help using preg_match_all options - Tokenizer Fixed a bug with reference and instanceof ## Version 0.5.3 2016-02-22 - Li Jing - Architecture More UT - Supports symlinks for neo4j's folder - Supports symlinks for 'code' folder in projects - Added upgrade command to check for exakat's available versions and upgrade - Analysis Spot CLI scripts - Undefined Interfaces avoids self, parent, static - Fixed bug in spotting undefined Interface - Variable Used Once in a method are not arguments - Added support for all structures in Double Assignation ## Version 0.5.2 2016-02-15 - Single Horned Rhinoceros King - Analysis Fixed functioncall detection with 'empty' - Refined 'Buried assignation' analysis - Fixed a bug when using definitions (class, trait, interface, functions...) - Better support for case-insensitive constants - - Tokenizer Fixed bug in use statement - Now spots PHP code in files without extension - Upgraded support for grouped Use statement - namespace may be a valid nsname part - Fixed bracket reports in do...while ## Version 0.5.1 2016-02-08 - King of Spiritual Touch - Architecture Added test in UT to skip incompilable sources - Stabilized tokenizer's UT (partial) - Report HTML protection in Devoops format - No display of negative stats - Added support for directives : wincache, xcache, apc, opcache - Added support for eaccelerator and openssl - Analysis New analysis : Spot unknown PHP directive names - Fixed Constants/MultipleDefinedConstants - Better detection of functioncalls (with List) - Better spotting of ini_set arguments - Unreachable code now finds die and exit - ObjectReference won't report references on scalar types - Revamped 'pregOptionE' analysis - Cleaned code with too many arguments - Removed useless print - Better report of eval() usage - Revamped 'Dynamic code' report - Fixed bug in Case/Default that are empty - Avoided sequences of sequences in Case/Default - Fixed Detection of classes' usage with extension - Tokenizer Fixed bracket detection on While and DoWhile - Detect void in DoWhile - Removed useless T_DIE token - Fixed fullcode processing for anonymous classes ## Version 0.5.0 2016-02-01 - Immortal of Antelope Power - Architecture Added support for HTTP API, through 'server' command. - Analysis Fopen modes checked - Redefined default, in class's properties - Tokenizer Fixed situation where echo and print used parenthesis (they don't) - Fixed rare but with instanceof and concatenation - Fixed support of integers in Gremlin - Fixed bug in addslashes and and $ protection order - Made Assignations more robust (no un-processed tokens) - Reduced the number of shell_exec usage => speed up - Finished support for relaxed keyword support in classes (PHP 7) ## Version 0.4.6 2016-01-25 - Immortal of Elk Power - Architecture New installation script with Vagrant and Ansible (Thanks Alexis!) - Updated documentation - Added a command to remove a project - Report Devoops reports has case-insensitive menu sort - Analysis Spot redefined properties, classes and methods. - Spot properties that may be turned private - Fixed special case in Wrong Number Of Arguments - Fixed 'OnePage' analysis - Tokenizer Finished support for relaxed keywords in classes - Sped up tokenizer by keeping counts of tokens in datastore - Fixed detection of CakePHP - Fixed special case with Labels - Fixed rare case with die() within ternary operator ## Version 0.4.5 2016-01-18 - Immortal of Tiger Power - Architecture Upgraded documentation - Default command is 'help' - Report Better version for FacetedJson report - Analysis New analysis that spots wrong type of argument in PHP internal functions - Fixed Isset With Constant for PHP 7 - Fixed a bug that limited query size during analysis (good for bigger projects) - Include variadic (...) to Variable Argument Number - Tokenizer Fixed a bug that blocked tokenizer when a analyzed script generated parse errors. - Added support for bazar, svn. - Fixed a bug in Nsnames at Loading time. ## Version 0.4.4 2016-01-11 - Crown Prince Mo'ang - Architecture Reviewed OnePage analysis - Dump as now an option to select Recipes - Dump forces line to be integer - Added a task to update a project's code (git only ATM) - Report Better check when opening database for report (more to come) - FacetedJson (and Json) report ignore non-unicode lines - Added 'search' box to facetedJson - Analysis Switch To Switch suggestions - Unused arguments patch for arguments used in methods - Unused properties doesn't mistake function static variable - Tokenizer All Nsnames are now build at Loading time - Constants may be calld 'const' - More relaxed syntax for methods (exit, include, eval...) - Foreach may use coalesce - Fixed an edge case with Closures in functioncall ## Version 0.4.3 2015-01-04 - Tuolong - Architecture Copyright year bump - Doctor reports memory_limit and php version consistency - Switched to rmDirRecursive - Report Removed old style reporting system - Analysis Fixed fileupload and filesystem directives reports - Added report of Environment variable usage - Added iconv_set_encoding to the list of directive usage - Extension analyzes now takes into account namespaces and traits - Analysiss all have severity and time to fix - Tokenizer ## Version 0.4.2 2015-12-22 - Red Boy - Architecture Published documentation on http://exakat.readthedocs.org - First version of the faceted report (-format Faceted) - Report First version of the faceted report (-format Faceted) - Fixed Dump that actually finishes after some time - Analysis Spot unused arguments - Fixed notInInterface() filter - Upgraded HtmlEntitiesCall ## Version 0.4.1 2015-12-14 - Azure Lion - Architecture Rebuild the report system, for speed and versatility. - Report Available format : JSON, Sqlite, XML, Text and HTML (Devoops). - Rules are now part of the documentation. - Analysis Upgraded 'Buried assignations' - Locally Unused also spots properties without visibility (but with definition) - Could be class constant, if the property is used at least once - Better detection of files that are Definitions only (fix at Namespace calls) - ++ is now correctly reported as isRead and isWritten in Arguments - Closure's use($x) are now reported in both context (calling and called) - Removed usage of 'back' method, that is blocking at high token counts - Tokenizer Fixed support for {} and {$ } inside strings - Fixed bug with Typehint, that prevented compilation - Fixed several (rare) edge cases with Sign and Staticproperties. - Fixed detection of closing tags ## Version 0.4.0 2015-12-07 - Lion Lynx Demon - Architecture Made PHP 7.0 the default (moved to 0.4.0) - Ran unit tests on PHPunit 5.1 - Added a background tasks to build report. Will allow for progressive report. - Report Rewrote the report from scratch. Should be finished next iteration. - New report is working for XML and Text report. - Analysis Added support for ext/pecl_http - Added several classic folders as ignored by default (change this in config.ini) - Create a check for functioncall (and not methods) - Spots join('', file()) - Safely ignoring some dynamic calls in undefined functions (Thanks Marc Delisle) - Removed ArrayAppend from double assignation - Tokenizer Fixed a bug when class was auto-referenced. - Fixed detecting Static properties when they are also arrays. - Fixed fatal errors for mal-formed octals ## Version 0.3.12 2015-11-30 - Nine Tailed Vixen - Architecture ProgressBar is now displayed during Analyze phase. - Report Report list of error messages used in the library - Analysis Omit eval with hardcoded strings - Exclude some index from _SERVER from the report (they are safe) - Exclude php://* files as hard coded path - Report usage of timestamp to calculate duration - Spots unused traits - Fixed support for big integers - Tokenizer First support for relaxed keywords in classes. More to come. - Checked UT on PHP 7 (Soon to become default version) - Fixed version detection in Tokenizer - Fixed fullnspath in Use expression; ## Version 0.3.11 2015-11-16 - Hu A'qi - Architecture Report external services files that may be in the repository - Report Report nested dirname calls (may be changed in PHP 7) - Analysis Better spotting of static loops - Don't confuse $globals and $GLOBALS - Tokenizer Rewrote support for As in classes. - Fixed arguments that were indexed as Void - Trimmed code ## Version 0.3.10 2015-11-09 - Silver Horned King - Architecture Centralized call to cypher. - Report Sped up several analyzes - Analysis Fixed naming bug with reflexion - Support class name in arrays, short syntax - Report Relay Functions - More PHP 7 incompatibilities reports - Tokenizer Support for 7.1 compilation (dev only) - Added cakephp to external libraries - Fixed parsing bug with static (as property definition) - Fixed 'count' in sequences from Function - Rewrote Argument detection (when there is no parenthesis) ## Version 0.3.9 2015-11-02 up - Golden Horned King - Architecture Cleaned code with Exakat - Analysis Refined report about double assignation - Fixed argument counting in Function Definition - Better support of array in Locally Used Properties - Updated Composer database - Tokenizer Fixed a bug that ignored Blocks - Fixed a rare bug with echo and the following arguments ## Version 0.3.8 2015-10-26 - Baihuaxiu - Architecture Cleaned too many display (they go to log now), leaving commandline empty (or -v) - A lot more PHP 7 incompatibilities spotted - Report Added the list of global variables in the projects (if any) - Fixed reports for PHP 5.2 (they were ignored) - Analysis Better handling of composer in unresolved classes - Spot setlocale with string (PHP 7) - Spot string unpacking (PHP 7) - Upgraded static method call, to avoid classes of the same family - Report eval without try/catch - Report preg_replace with /e - Fixed report for empty list() - Spot hexadecimal in strings - Report usort (and co) as incompatibilities between PHP 7 and 5 - Tokenizer Fixed edge case with Sign and namespaced function - Added xajax, adodb and gacl as common library - Fixed arguments in short array syntax - Fixed case where [3] was spotted inside a string ## Version 0.3.7 2015-10-19 - Yellow Robe Demon - Architecture Added and reviewed many UT. More stability. - Report Fixed the report of the actual version of PHP being used. - Non-run analysis are not marked with a stethoscope - Report now report closures and not the containing method - Removed some dashboard that would generate empty links - Analysis Better spot of blocks inside Alternative syntax - Speed up method spotting - Fixed properties which were mistaken with deep definitions - Tokenizer Fixed fullcode for Typehint - Removed Ppp and moved it to Visibility ## Version 0.3.6 2015-10-12 - White Bone Demon - Architecture Large speed up at Parsing stage, for large projects - Added git informations in Doctor - Tokenizer Changed processing for Arguments. - Support for more PHP 7 features, including Use Grouping, - Fixed support for ~ - Simplified ::class handling ## Version 0.3.5 2015-10-06 - Mingyue - Architecture Reported usage of array constants, improving backward compatibility - Checked running on PHP 7 - Report Added Definition annex - Fixed 'version incompatible' report that was mistaken with 'no result' - List all directives being modified in the code - List more directives that should be set for production. - Analysis Reworked the Themes about compatibility. - Added many tests for PHP 7.0 compatibility - Sped up UsedMethod analysis - Added support for PHP 7 feature : Unicode Escape Sequences, New functions/classes/interfaces, Removed Functions, - Tokenizer Changed processing for Empty PHP code - Support Variable Indirection for both PHP 5 and 7 (depends on exec version) - Avoid ignoring all code when finding External Libraries - Fixed edge cases with declare() when it is conditional. - Support for PHP 7's f()()() ## Version 0.3.4 2015-09-28 up - Qingfeng - Architecture Added token_limit configuration to avoid running too large project (default is 1 000 000) - Several new tools for internal consistency check. - Removed support for neo-contrib's gremlin plugin - Report Report libraries that were found and ignored - Analysis Sped up queries that required previous analysis or multiples atoms - Spot global keywords inside loops (perf) - Better spotting of Composer classes - Report double assignations - Tokenizer Added support for Anonymous classes (PHP 7) - Fixed namespace manipulations (They weren't lower case) - Mark constants as fail back globals or local to the namespace - Support Null Coalesce operator (PHP 7) - Fixed rare case for empty strings and noDelimiter ## Version 0.3.3 2015-09-21 - Immortal Zhenyuan - Architecture Removed some shell stderr that leaked to the main script - Report Added the list of used analysis - favicon is now used in the report (Devoops) - Fixed count report for Else - Fixed directive reports for trader, bcmath and ldap. - Analysis Rebuild the composer database - Fixed htmlentities analyze - Spot usage of 'substr($s, $p, +/- 1)' and recommend '$s[$p]' - Tokenizer Fixed Multiplication with instantiation ## Version 0.3.2 2015-09-14 - Tiger Vanguard - Report Added link back from analysis to its themes. - Analysis Useless Returns are now Trait compatible - Optimized Composer validation - Removed IsKnownVendor analyze (replaced by Composer) - Spot inconsistent concatenations ("$a b".$c) - Tokenizer Fixed situation where forgotten white spaces didn't have a file - Removed DELETE and S_STRING index - Fixed compatibility with Debian (shell commands) - Added UT for and / && precedence versus = - Fixed identification of empty instructions (Functions / Closure have different behaviors) ## Version 0.3.1 2015-09-03 - Yellow Wind Demon - Architecture Removed usage of Everyman dependencies - Added support for Neo4j Authentication - Added a JobQueue - Cleaned code with exakat itself - Report Added Dump to SQLITE format for custom manipulations of the results - Added new collection of rules for Calesthenics (dev) - Updated composer database - Now reporting found Composer. - Analysis Fixed Compilation spotting - Tokenizer Fixed an edge case with Sign, when used in a concatenation ## Version 0.3.0 2015-Aug-25 - Lingxuzi - Architecture Moved to Thinkaurelius's gremlin plug-in, Neo4j 2.2.4 and Java 8. - Report Added a view by File - Added sorting for results (by file and by analyze) - Analysis Spot functions whose results should be checked before they are used - Spot breaks/continue out of a loop - Exports all the results in a dump.sqlite file - Tokenizer Fixed a minor bug with ::class (messed up the {} counts) - removed dependency to Everyman's Neo4j classes. - Added a step that removes big and identifiable libraries in PHP (such as tcpdf, jpgraph, etc..) ## Version 0.2.5 2015-Aug-17 - Scholar in a White Robe - Report List the files that are ignored in the annex - Analysis Updated Knowledge Database for memcache, aliases, zlib, standard - Added more directives to Review - Added support for xhprof - Tokenizer Fixed bug with Else (Not-alternative) - Fixed Sequence creation with If-Then - Yield may be assigned - Removed one Tokenizer's operation (filterOut2) - Fixed priorities with Concatenation, Multiplication, Additions - Process Echo and Print separately - Automatically removes common bundled libraries to reduce app size ## Version 0.2.4 2015-06-22 - Black Wind Demon - Analysis Rebuild the composer database - Lots of new extensions supported : ev, libevent, event, php-ast, wikidiff2, proctitle, inotify, ibase, amqp, geoip, output buffering, - Report errors when non-variables are returned by reference - Marked more analyzes for PHP 7 - Fixed Unpreprocess structures with split - Upgraded spotting for useless parenthesis - Added a check ++$i vs $i++; - Exclude abstract methods from Variables Used Once - Added new directives - Also check for ASP Tags - Tokenizer Fixed the fullpath for functions when they are not defined in the code - Upgraded support for Return Type (PHP 7.0+) - error_reporting with -1 is OK - Fixed a precedence problem with & and && - Refactored Ifthen token to support return type - Added a kill command when cleaning Database ## Version 0.2.3 2015-06-22 - Techu Shi - Analysis Report usage of Return Typehint, and Scalar Typehint - Report usage of classes that used to return null on new - Report useless abstract classes - Tokenizer Upgraded 'init' command, to handle various VCS - Added support for Return Typehint ## Version 0.2.2 2015-06-16 - Xiong Shangjun - Analysis Now spots short assignations - More UselessInstructions spotted - Ignore Unset as modified values in loops - Tokenizer Added support for PHP7 new tokens (T_SPACESHIP, T_COALESCE, T_YIELD_FROM) - Split loading into more .csv files for lighter and more robust queries - Better support for arrays [1,2,3] as functioncall (just like array()) - Process tokens by batches of 800 - Clean vertex at each queries, not Sequence ## Version 0.2.1 2015-06-02 - General Yin - Analysis sizeOf may have 2 arguments - 2 clearPHP link added in documentation - Tokenizer Fixed bug with Bitshift and Addition - Fixed bug with Sequence when merging sequences - Fixed bug with String and Addition - Fixed Visibility in Use instruction - Foreach accepts Constants as Source - Fixed special case for nested IfThen ## Version 0.2.0 2015-05-15 - Demon of Confusion - First version --- # My Account Source: https://www.exakat.io/my-account/ --- # CI CD Token Source: https://www.exakat.io/ex/ci-cd-token/ --- # What is static analysis Source: https://www.exakat.io/what-is-static-analysis/ --- # Use Case Source: https://www.exakat.io/php-static-analysis-use-case/ --- # Online Demo Source: https://www.exakat.io/online-demo/ --- # Formation – Adopter le typage fort en PHP Source: https://www.exakat.io/formation-adopter-le-typage-fort-en-php/ --- # Wishlist Source: https://www.exakat.io/ex/wishlist/ [yith_wcwl_wishlist] --- # No Access Source: https://www.exakat.io/no-access/ --- # project too large Source: https://www.exakat.io/project-too-large/ --- # Home page old Source: https://www.exakat.io/home-page/ --- # Product Source: https://www.exakat.io/product/ --- # Terms Source: https://www.exakat.io/terms/ --- # Legal terms – Mentions légales Source: https://www.exakat.io/legal-terms-mentions-legales/ --- # Prevent security issues Source: https://www.exakat.io/prevent-security-issues/ --- # WordPress Demo Source: https://www.exakat.io/online-demo/wordpress-demo/ --- # Get Expertise Source: https://www.exakat.io/get-php-expertise/ ### EXAKAT CUSTOMIZATION ### EXAKAT SUPPORT ### APPLICATION AUDIT --- # FAQ Source: https://www.exakat.io/faq/ --- # Exakat Enterprise Self-Hosted Source: https://www.exakat.io/exakat-enterprise-self-hosted/ --- # exakat API Source: https://www.exakat.io/exakat-api/ --- # Cart Source: https://www.exakat.io/cart/ --- # Contact exakat Source: https://www.exakat.io/contact-us/ ## Contact exakat Leave us a message, we'll get back at you. We'll start our conversation from here.   [contact-form-7 title="Contact-Us"] --- # Control the quality you need Source: https://www.exakat.io/control-the-quality-you-need/ --- # Cloud Project Source: https://www.exakat.io/cloud-project/ --- # Auto Draft Source: https://www.exakat.io/auto-draft/ --- # Souscription Account Source: https://www.exakat.io/souscription-account/ **text** --- # Provider Account Source: https://www.exakat.io/provider-account/ ## Welcome back ! #### You have already an account with the same email in EXAKAT Cloud. Please login before to add your repositories to your Exakat Account. --- # Working on report Source: https://www.exakat.io/working-on-report/ --- # Integrate in your CI Source: https://www.exakat.io/integrate-in-your-ci/ --- # Exakat gallery Source: https://www.exakat.io/exakat-gallery/ --- # Exakat Source: https://www.exakat.io/ex/empty/ --- # exawas API Source: https://www.exakat.io/exawas/ --- # PHP distribution evolution Source: https://www.exakat.io/php-distribution-evolution/ PHP includes a large number of extension in its standard distribution. Those are available in the php-src repository and the source download, the other extensions are available in [PECL](http://pecl.php.net/). ![](https://178.62.231.40/wp-content/uploads/2019/06/circuit.320.jpg)php standard distribution evolution In PHP 5.0, there was 84 standard extensions, and there will be 70 in PHP 7.4. The number of extension has been dropping regularly. Extensions are moved to PECL when they are not ported to the new version, or are not maintained anymore. ![](https://178.62.231.40/wp-content/uploads/2019/06/php.standard.distribution.png)Number of extension in PHP standard distribution, from 5.0 to 8.0 In PHP 7.4. interbase (ibase) and wddx are moved to PECL, while ffi (Foreign Function Interface) is added to the distribution. That's a net reduction of 1 extension. Here is the detailled evolution of all extensions, since PHP 5.0. | | 8.0 | 7.4 | 7.3 | 7.2 | 7.1 | 7.0 | 5.6 | 5.5 | 5.4 | 5.3 | 5.2 | 5.1 | 5.0 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | [bcmath](https://www.php.net/bcmath) | | | | | | | | | | | | | | | [bz2](https://www.php.net/bzip2) | | | | | | | | | | | | | | | [calendar](https://www.php.net/calendar) | | | | | | | | | | | | | | | [com_dotnet](https://www.php.net/dotnet) | | | | | | | | | | | | | | | cpdf | | | | | | | | | | | | | | | [ctype](https://www.php.net/ctype) | | | | | | | | | | | | | | | [curl](https://www.php.net/curl) | | | | | | | | | | | | | | | [date](https://www.php.net/datetime) | | | | | | | | | | | | | | | [dba](https://www.php.net/dba) | | | | | | | | | | | | | | | [dbase](https://www.php.net/dbase) | | | | | | | | | | | | | | | [dbx](https://www.php.net/dbx) | | | | | | | | | | | | | | | [dio](https://www.php.net/dio) | | | | | | | | | | | | | | | [dom](https://www.php.net/dom) | | | | | | | | | | | | | | | [enchant](https://www.php.net/enchant) | | | | | | | | | | | | | | | [ereg](https://www.php.net/ereg) | | | | | | | | | | | | | | | [exif](https://www.php.net/exif) | | | | | | | | | | | | | | | [fam](https://www.php.net/fam) | | | | | | | | | | | | | | | [fbsql](https://www.php.net/fbsql) | | | | | | | | | | | | | | | [fdf](https://www.php.net/fdf) | | | | | | | | | | | | | | | [ffi](https://www.php.net/ffi) | | | | | | | | | | | | | | | [fileinfo](https://www.php.net/fileinfo) | | | | | | | | | | | | | | | [filepro](https://www.php.net/filepro) | | | | | | | | | | | | | | | [filter](https://www.php.net/filter) | | | | | | | | | | | | | | | [ftp](https://www.php.net/ftp) | | | | | | | | | | | | | | | [gd](https://www.php.net/image) | | | | | | | | | | | | | | | [gettext](https://www.php.net/gettext) | | | | | | | | | | | | | | | [gmp](https://www.php.net/gmp) | | | | | | | | | | | | | | | [hash](https://www.php.net/hash) | | | | | | | | | | | | | | | [hwapi](https://www.php.net/hwapi) | | | | | | | | | | | | | | | [iconv](https://www.php.net/iconv) | | | | | | | | | | | | | | | [imap](https://www.php.net/imap) | | | | | | | | | | | | | | | informix | | | | | | | | | | | | | | | ingres_ii | | | | | | | | | | | | | | | [interbase](https://www.php.net/ibase) | | | | | | | | | | | | | | | [intl](https://www.php.net/intl) | | | | | | | | | | | | | | | [json](https://www.php.net/json) | | | | | | | | | | | | | | | [ldap](https://www.php.net/ldap) | | | | | | | | | | | | | | | [libxml](https://www.php.net/libxml) | | | | | | | | | | | | | | | [mbstring](https://www.php.net/mbstring) | | | | | | | | | | | | | | | [mcrypt](https://www.php.net/mcrypt) | | | | | | | | | | | | | | | [mcve](https://www.php.net/mcve) | | | | | | | | | | | | | | | [mhash](https://www.php.net/mhash) | | | | | | | | | | | | | | | [mime_magic](https://www.php.net/mime_magic) | | | | | | | | | | | | | | | [ming](https://www.php.net/ming) | | | | | | | | | | | | | | | [mnogosearch](https://www.php.net/mnogosearch) | | | | | | | | | | | | | | | [msession](https://www.php.net/msession) | | | | | | | | | | | | | | | [msql](https://www.php.net/msql) | | | | | | | | | | | | | | | [mssql](https://www.php.net/mssql) | | | | | | | | | | | | | | | mysql | | | | | | | | | | | | | | | [mysqli](https://www.php.net/mysqli) | | | | | | | | | | | | | | | [mysqlnd](https://www.php.net/mysqlnd) | | | | | | | | | | | | | | | [ncurses](https://www.php.net/ncurses) | | | | | | | | | | | | | | | [oci8](https://www.php.net/oci8) | | | | | | | | | | | | | | | [odbc](https://www.php.net/odbc) | | | | | | | | | | | | | | | [opcache](https://www.php.net/opcache) | | | | | | | | | | | | | | | [openssl](https://www.php.net/openssl) | | | | | | | | | | | | | | | [oracle](https://www.php.net/oracle) | | | | | | | | | | | | | | | ovrimos | | | | | | | | | | | | | | | [pcntl](https://www.php.net/pcntl) | | | | | | | | | | | | | | | [pcre](https://www.php.net/pcre) | | | | | | | | | | | | | | | [pdo](https://www.php.net/pdo) | | | | | | | | | | | | | | | [pdo_dblib](https://www.php.net/pdo_dblib) | | | | | | | | | | | | | | | [pdo_firebird](https://www.php.net/pdo_firebird) | | | | | | | | | | | | | | | [pdo_mysql](https://www.php.net/pdo_mysql) | | | | | | | | | | | | | | | [pdo_oci](https://www.php.net/pdo_oci) | | | | | | | | | | | | | | | [pdo_odbc](https://www.php.net/pdo_odbc) | | | | | | | | | | | | | | | [pdo_pgsql](https://www.php.net/pdo_pgsql) | | | | | | | | | | | | | | | [pdo_sqlite](https://www.php.net/pdo_sqlite) | | | | | | | | | | | | | | | pfpro | | | | | | | | | | | | | | | [pgsql](https://www.php.net/pgsql) | | | | | | | | | | | | | | | [phar](https://www.php.net/phar) | | | | | | | | | | | | | | | [posix](https://www.php.net/posix) | | | | | | | | | | | | | | | [pspell](https://www.php.net/pspell) | | | | | | | | | | | | | | | [readline](https://www.php.net/readline) | | | | | | | | | | | | | | | [recode](https://www.php.net/recode) | | | | | | | | | | | | | | | [reflection](https://www.php.net/reflection) | | | | | | | | | | | | | | | [session](https://www.php.net/manual/en/book.session.php) | | | | | | | | | | | | | | | [shmop](https://www.php.net/shmop) | | | | | | | | | | | | | | | [simplexml](https://www.php.net/simplexml) | | | | | | | | | | | | | | | [snmp](https://www.php.net/snmp) | | | | | | | | | | | | | | | [soap](https://www.php.net/soap) | | | | | | | | | | | | | | | [sockets](https://www.php.net/sockets) | | | | | | | | | | | | | | | [sodium](https://www.php.net/sodium) | | | | | | | | | | | | | | | [spl](https://www.php.net/spl) | | | | | | | | | | | | | | | [sqlite](https://www.php.net/sqlite) | | | | | | | | | | | | | | | [sqlite3](https://www.php.net/sqlite3) | | | | | | | | | | | | | | | [sybase](https://www.php.net/sybase) | | | | | | | | | | | | | | | sybase_ct | | | | | | | | | | | | | | | sysvmsg | | | | | | | | | | | | | | | sysvsem | | | | | | | | | | | | | | | sysvshm | | | | | | | | | | | | | | | [tidy](https://www.php.net/tidy) | | | | | | | | | | | | | | | [tokenizer](https://www.php.net/tokenizer) | | | | | | | | | | | | | | | w32api | | | | | | | | | | | | | | | [wddx](https://www.php.net/wddx) | | | | | | | | | | | | | | | [xml](https://www.php.net/xml) | | | | | | | | | | | | | | | [xmlreader](https://www.php.net/xmlreader) | | | | | | | | | | | | | | | [xmlrpc](https://www.php.net/xmlrpc) | | | | | | | | | | | | | | | [xmlwriter](https://www.php.net/xmlwriter) | | | | | | | | | | | | | | | [xsl](https://www.php.net/xsl) | | | | | | | | | | | | | | | yp | | | | | | | | | | | | | | | [zip](https://www.php.net/zip) | | | | | | | | | | | | | | | [zlib](https://www.php.net/zlib) | | | | | | | | | | | | | | --- # Top 10 PHP Classic Traps Source: https://www.exakat.io/top-10-php-classic-traps/ ## Exakat Top 10 report You can check all the errors that are mentioned in the above presentation with the exakat engine. Configure your project with the report 'Top10' or request the specific report : php exakat.phar report -p my_project -format Top10 --- # Quick Introduction Source: https://www.exakat.io/quick-introduction/ --- # Exakat video Source: https://www.exakat.io/exakat-video/ --- # webhook Source: https://www.exakat.io/wbhook/ --- # wbinit Source: https://www.exakat.io/wbinit/ --- # My reports Source: https://www.exakat.io/my-reports/   [cloud_listvcs] --- # Using a vector database with PHP Source: https://www.exakat.io/using-a-vector-database-with-php/ # Using a vector database with PHP PHP has roughly 80 array functions: eight - zero. `array_map`, `array_walk`, `array_reduce`,`array_filter`, `array_each`... wait, that last one was deprecated and removed in PHP 7.2. Or not? And some new functions came up in PHP 8.5, last november. The real problem is discoverability. You know the task, "find the first element matching a condition", but you can't remember if it's `array_find`, `array_search`, `array_filter`, or something that doesn't exist yet and you'll have to write yourself. You end up on the eternal manual [php.net](https://www.php.net/manual), scrolling through the full list like it's a 2003 API reference, which it kind of is, hoping that the right function will jump out of the dark at the best moment. Vector databases solve this with a different approach entirely. Instead of matching text, they match meaning. You describe what you want, and you get back the semantically closest functions, regardless of the exact words. So, today we'll build exactly that using [Vektor](https://github.com/centamiv/vektor), a pure PHP file-based vector database, and [Ollama](https://ollama.com/) for local embeddings. In a nutshell: no cloud; no API key; no monthly bill; no privacy invasion. Just PHP doing what PHP does: being surprisingly capable. ## What we're building A command-line tool that: - Reads the local PHP documentation from HTML files - Extracts every array function name, signature, and description - Converts each into a 768-dimensional embedding vector using Ollama - Stores those vectors in Vektor - Answers natural-language questions like "sort array while preserving key-value association" The result is a local semantic search engine over PHP's array API. Entirely file-based, running offline, and using a few hundred kilobytes on disk. ## Prerequisites - PHP 8.2+, and try it on PHP 8.6 if you want, it is out - [Composer](https://getcomposer.org/) - [Ollama](https://ollama.com/) installed and running locally - The PHP documentation downloaded locally (more on this below) Once Ollama is installed, pull the embedding model: ollama pull nomic-embed-text `nomic-embed-text` produces 768-dimensional vectors, runs entirely on-device, and is purpose-built for semantic text similarity. Verify that it is working: curl -s http://localhost:11434/api/embeddings \ -d '{"model":"nomic-embed-text","prompt":"test"}' | \ php -r '$d=json_decode(file_get_contents("php://stdin"),true);echo count($d["embedding"]);' # Expected: 768 ## Step 1: Download the PHP documentation Here is the step where we resist the urge to write a scraper, which is the correct instinct. php.net provides the complete documentation as downloadable archives at [https://www.php.net/download-docs.php](https://www.php.net/download-docs.php). Choose "Many HTML files". This gives you one HTML file per manual page, which is exactly what we need for per-function parsing. Download and extract: # The exact filename varies with date; find it on the download page wget https://www.php.net/distributions/manual/php_manual_en.tar.gz tar -xzf php_manual_en.tar.gz mv php-chunked-xhtml php_manual_en # You now have: php_manual_en/ We used the English manual, but you may also use any of the translations. Just update the language once we reach the queries. Naturellement! Array functions live in files named `function.array-*.html`: ls php_manual_en/function.array-*.html | wc -l # 79 Seventy-nine functions. One HTML file each. Let's parse them. ## Step 2: Project setup mkdir php-array-search cd php-array-search composer require centamiv/vektor mkdir data # Symlink or copy the manual directory ln -s /path/to/php_manual_en ./php_manual_en In the end, we will have the following final structure: php-array-search/ ├── data/ # Vektor binary files (written automatically) ├── php_manual_en/ # Downloaded PHP docs ├── parse.php # HTML extraction functions ├── parse_one.php # HTML extraction of one ├── embed.php # Ollama embedding helper ├── index.php # Indexing script (run once) ├── search.php # Search CLI (run repeatedly) ├── optimize.php # Maintenance └── vendor/ ## Step 3: Parse the PHP documentation The downloaded manual is machine-generated from DocBook XML, so the HTML structure is consistent and reliable. Each function page carries the function name, a one-line purpose, the signature, parameter descriptions, and the return value description. We want all of it. The `buildEmbedText` function is more than optional decoration. Embedding just `"array_map"`gives you a vector that will cluster with `"map"` and `"array"`. Embedding the full description, with its name, purpose, signature, what the parameters do, what is returned, gives a vector that encodes what the function actually does. That semantic richness is what makes the search useful. If you want to verify the parser on a single file before indexing everything, this snippet is your friend: Expected output (abbreviated): Array ( [name] => array_map [purpose] => Applies the callback to the elements of the given arrays [signature] => array array_map(?callable $callback, array $array, array ...$arrays) [params] => A callable to run for each element in each array. ... [returns] => Returns an array containing the results of applying the ... ) --- Embed text --- array_map: Applies the callback to the elements of the given arrays. Signature: ... If the XPath selectors produce empty strings for your downloaded version, inspect one file with `xmllint --html --xpath '//span[@class="refname"]' php_manual_en/function.array-map.html`and adjust the selectors accordingly. The structure is consistent across the manual, so once one file works, all of them will. ## Step 4: Embeddings with Ollama Nothing exotic here. The code is mainly a curl call to the local Ollama API, which returns the embedding: ## Step 5: Indexing This is where it all comes together. One important constraint to understand before writing a single line: Vektor's binary file layout is baked to a fixed record width determined by the vector dimension. The default is 1536, and it is sized for OpenAI embeddings. We are using 768-dimensional vectors from `nomic-embed-text`. If you forget to set the dimension before instantiating the `Indexer`, it will silently write vectors into a binary file with the wrong size. The reads then return garbage, and there will be no error message: just meaningless results, also known as false positives. We also say hallucinations nowadays, although the fashion is already fading away. Just set dimensions first. Run it: php index.php Note that usually, index.php is the default file in an online PHP application. Here, it is the actual semantic meaning of the verb: do an indexation. Expected output: Indexing array_chunk... done Indexing array_column... done Indexing array_combine... done ... Indexing usort... done Indexed : 59 Skipped : 0 Vectors on disk : 59 Graph nodes : 59 Storage : 200 KB 59 functions, ~200 KB on disk. A complete, self-contained knowledge base. The indexing run takes 30–60 seconds on a modern laptop. This is due to Ollama generating embeddings sequentially. You can take the time to speed that up later, it is not critical in our case. And you only do it once. Peek at what Vektor created in `data/`: ls -lh data/ # vector.bin : raw float arrays, fixed record width # graph.bin : HNSW graph connections # meta.bin : ID-to-offset BST (no RAM map, pure disk seeks) # payload.bin : serialized metadata (JSON blobs) Four binary files, no external process, no daemon. The entire database is those four files. ## Step 6: Search Now the good part: Use it from the command line: php search.php "find elements in common between two arrays" php search.php "sort array while keeping key-value association" php search.php "apply a function to transform every element" php search.php "remove duplicate values" php search.php "add element to the beginning" ## What the vectors reveal Let's look at real output and see what's interesting about it. ### "find elements in common between two arrays" [0.6704] array_find array_find — Returns the first element satisfying a callback function [0.6568] array_intersect array_intersect — Computes the intersection of arrays [0.6506] array_combine array_combine — Creates an array by using one array for keys and another for its values [0.6472] array_find_key array_find_key — Returns the key of the first element satisfying a callback function [0.6470] array_intersect_key array_intersect_key — Computes the intersection of arrays using keys for comparison The entire intersect family, ranked by specificity. `array_diff` appears at #5, because "finding what is in common" and "finding what is different" are semantically close operations. The model doesn't know PHP, but it knows language. That result is not wrong; it is useful. And don't let any superior AI engine tell you otherwise: what we're doing here is a small scope version of the global race that we see around us. Nothing more. ### "sort array while keeping key-value association" [0.7179] asort asort — Sort an array in ascending order and maintain index association [0.6950] ksort ksort — Sort an array by key in ascending order [0.6896] sort sort — Sort an array in ascending order [0.6855] arsort arsort — Sort an array in descending order and maintain index association [0.6774] uasort uasort — Sort an array with a user-defined comparison function and maintain index association The two associative variants lead, followed by the key sorts. `usort` does not appear because it explicitly does not preserve key association: nice. The model picks this up from the description text. This is the search working as intended. ### "apply a callback to an array" [0.7834] array_map array_map — Applies the callback to the elements of the given arrays [0.7469] array_reduce array_reduce — Iteratively reduce the array to a single value using a callback function [0.7313] array_walk array_walk — Apply a user supplied function to every member of an array [0.7220] array_all array_all — Checks if all array elements satisfy a callback function [0.7218] array_filter array_filter — Filters elements of an array using a callback function This one is philosophically interesting. `array_map`, `array_walk`, and `array_reduce` all "apply a function to elements of an array." They are semantically nearly identical: the difference is behavioral. `array_map` returns a new array, `array_walk` modifies it in place, `array_reduce`and then returns a scalar. And the three descriptions use similar language, so they cluster tightly. `array_filter` also takes a callback but its purpose is selection, not transformation: that could explain its absence from that list. The search is surfacing a meaningful semantic grouping that the PHP manual's alphabetical listing completely obscures. ### "remove duplicate values" [0.6298] array_unique array_unique — Removes duplicate values from an array [0.5445] array_count_values array_count_values — Counts the occurrences of each distinct value in an array [0.5281] array_unshift array_unshift — Prepend one or more elements to the beginning of an array [0.5150] array_reduce array_reduce — Iteratively reduce the array to a single value using a callback function [0.5080] array_combine array_combine — Creates an array by using one array for keys and another for its values `array_unique` is first: well, but of course. `array_count_values` is second. The model has no knowledge of PHP idioms, yet `array_count_values` still ended up near `array_unique` in the vector space because both descriptions discuss array values and their uniqueness. Coincidence? Possibly. Useful? Definitely. ## A word on scores Cosine similarity in this range does not have universal absolute meaning: what matters is the relative ranking. Roughly: | Score | Meaning | | ----- | ------- | | > 0.90 | Near-identical purpose | | 0.80–0.90 | Same functional family | | 0.70–0.80 | Related operations | | < 0.70 | Loosely connected at best | If all your results are clustering below 0.65, your embedding text is too sparse. Go back to `buildEmbedText` and add more context. The parameter descriptions have a large impact. ## Step 7: Maintenance A technical note here: Vektor uses soft deletes: when you call `insert()` with an existing ID, the old record is tombstoned rather than overwritten. Over multiple re-indexing runs, the binary files grow with dead records. The `Optimizer` compacts them: php optimize.php Run this after any bulk re-indexing. It rebuilds the HNSW graph from scratch and reclaims space from all the tombstoned records. ## Going further Expand to all PHP built-in functions. The manual has HTML files for ~1000 built-in functions. Change the glob: Expect ~10 minutes of embedding time and a few MB of vector data. The search stays fast: HNSW is a logarithmic-time algorithm; doubling the index does not double query time. Run Vektor as a shared HTTP API. Vektor ships with a built-in controller for HTTP mode. Index once from a CLI script and query from anywhere: cp .env.example .env # Set VEKTOR_API_TOKEN= and VEKTOR_DIMENSIONS=768 in .env php -S 0.0.0.0:8000 -t public Just think about security, as you'll be on the internet, not just running a local experimentation. Not the same pond. Then search via HTTP, useful for a team tool or a web UI: curl -s http://localhost:8000/search \ -H "Content-Type: application/json" \ -d '{"vector": [...768 floats...], "k": 5, "include_metadata": true}' Try a larger embedding model. `nomic-embed-text` is fast and small. `mxbai-embed-large` produces 1024-dimensional vectors with generally better retrieval accuracy for English text. Remember: changing dimensions means deleting `data/` and re-indexing from scratch: the binary record width changes. Embed richer content. The PHP manual pages include code examples. Appending a representative code snippet to the embed text often improves recall dramatically, because the example captures usage patterns that the prose description doesn't state explicitly. `array_walk` and `array_map` sound similar in description; in practice, one mutates and the other doesn't: a code example makes that difference visible to the embedding model. ## Your mileage may vary I took the values for this post while writing it. It applies to the datasets that I used and mentioned, in early days of July 2026. The results might vary a lot depending on the actual LLM used (both version, type, and number of parameters), the available PHP functions and their documentation, etc. The scores also may vary. ## The takeaway PHP has had the pieces for this kind of tooling for a while. Vektor provides the vector storage and the HNSW index. Ollama provides the embeddings. The PHP manual provides the data. You provide fifteen minutes of setup time. The result is a local, zero-dependency semantic search over PHP's own API. No cloud, no subscription, no rate limits: just four binary files on disk and a curl call to your laptop. PHP has eighty array functions, most of them named in ways that made sense in 1997. A vector database doesn't care. Ask it what you want in plain language, and it will find the function you are looking for, even the one you didn't know existed. --- # Remove PHP dead code Source: https://www.exakat.io/remove-php-dead-code/ [![](https://www.exakat.io/wp-content/uploads/2014/06/toothbrush.320.jpg)](https://www.exakat.io/wp-content/uploads/2014/06/toothbrush.320.jpg) # Detecting dead code in PHP PHP applications are under constant evolution. The code tents to grow bigger, more complex, and finally, to collect dust : this also know as dead code. Dead code is actual code that is not being used in production, even if the code is deployed. It is important to remove dead code. Missing code is actually easy to spot : at some point, a part of the application will fail, and die hard, with an error message. On the other hand, supplementary code just sit idle, doing nothing. Unused code never report bugs, and it may stay there for long time. The nasty effect of dead code on the application is at the developer level : more code to read, more code to maintain, more clutter to understand when a bug strikes somewhere. It also have an impact on PHP execution time and memory consumption. Here is a list of type of dead code that is good to remove. ## The comment PHP code in comments does happen quite often. When debugging it is easier to put code into comments. When attention shift to another part of the code, those comments are left there. They shouldn't be there. The best is to remove them as soon as the debug is done, and whenever they are spotted in the code. VCS will allow to go back to previous code, so there is not reason to keep it twice. The thoughtlessness This kind of code is actually some coding error. It may take the following shape : `function x () { return 1 ; $a++ ; }` The increment will never be reached, as the function is finished before. This kind of situations happens with various keywords : return, break, continue, die, exit. Sometimes also with structures such as if or while, with constant values in the condition : this is a variation of the previous section. At a higher level, it also occurs with functioncall or methodcall, when those call die or exit. When all the code is at the same level, like in the above example, it may be cleaned easily : either remove unattainable code, either remove the terminating instruction. ## Obsolete definitions Constants, functions, classes, interfaces or traits may be defined but not used. The evolution of the code means that some of those structures will be created, used then unused. The trick is that no error will be emitted in case of unused functions. Each of those structures have specific ways of being used and all of them must be checked. - Constants are directly used in code, or may be reached with the constant() function - Functions are called in the code. They may be dynamically called with call_user_func (and similar), or with variables $function(). - Traits are implemented in classes or traits. - Interfaces are implemented in classes or interfaces, or checked with instanceof. - Classes are used in new, extends, catch structures, typehint structures, or instanceof calls. Some of those calls are also dynamical, such as new or instanceof. To remove those structures, all the code has to be checked. If no occurrence is found, then it may be removed. ## Classes features Class constants, properties and methods are the same as the above structure, but defined and used within a limited scope. Finding this scope is difficult in PHP, although, after finding it, the same tactics as the previous one may be used. Unused private methods have to be unused by the class itself. Unused protected methods are not used by the class, the parents and the extension classes. Public methods have the same requirements, plus no usage from objects point of view. The two first one may be checked with static auditing. Public values require a full application scan, and are more difficult to find. Too bad, PHP set all methods public by default. This is definitely costly in the long run. ## Unused inclusion If the application rely on autoload, the classes will be loaded just in time. When PHP needs a class, as stated above in the Obsolete structures section, it will use autoload() to find it and load it. There is no waste of included code with this approach. Unused classes means that the class is never mentioned in the code. This is pretty straightforward process. On the other hands, if files are included, we need to check if any of the structures that it are defined in that file, are used. For example, tools libraries gather large number of utility functions. If any one of those functions is used in the calling script, then the file is not dead code, and the inclusion must stay. Otherwise, it may be dropped. Cleaning those libraries must be done at the function level, as presented above. Beware of inclusion that have global code : such code will be executed at inclusion time, and removing the inclusion, even not using the defined structures, might break some code. ## Never included files Finally, whole source files may be dead code, simply because they are never included, nor directly called. It is easy to detect such files, as they end up empty when pruning obsolete classes or functions : when cleaning a file, if you're removing too many functions or classes, you wonder if the whole file will still needed. ## Unit tests Finally, when cleaning code, do not forget to remove the associated tests. It is a common pitfall : code is kept in repository, because it is used in the another part of the application : this part are … the unit tests ! This ends up as a chicken-egg problem : Unit tests needs to call the class, then the class is used and needs.... more tests. ## Finally Removing dead code is a matter of daily hygiene. The most difficult part is to overcome the scare. Scare of ridicule, that will be the terrible reward of too much removal and too little checking. I like to clean code by little touches: when I spot some that looks dead, I review the above checklists and make a specific commit for it so I can revert it quickly. I manage to remove 5 to 20 lines of code every day, in one commit. And it does feel as good as brushing one's teeth ! --- # 5 points for a self PHP code review Source: https://www.exakat.io/5-points-for-self-php-code-review/ [![checklist.320](http://www.exakat.com/wp-content/uploads/2014/05/checklist.320.jpg)](http://www.exakat.com/wp-content/uploads/2014/05/checklist.320.jpg)Reviewing code is like checking your own copy before handing in an exam : last exam I took was driving test (incredible, I know), and I passed by 0 the 100 questions quiz. I do remember changing three or four answers while reviewing the answers, and even as I don't know for sure about it, I have the feeling this saved the day. Reviewing the code have the same effect, anytime I find something that 'was not supposed to be there'. Luckily, this happens rarely, but when it happens, it feels much better about the final version of the code. Of course, there is also the unspoken relief of keeping my ego unbruised by a typo (and that's why I won't give examples here). There are 5 types of errors I like to check when I'm reviewing my code. - **D****ead code** : this is code that can't be reached. The two common situations I find are multiple returns in a sequence, and constants conditions in an if. `if (1 || $o->checkForStatus()) { return $o->getId() ; return $o->getSize() ; }` The above code has it all. The second return is often some temporary code, which was forgotten. The constant condition is a potential debug line. Both are usually easy to remove. - **I****mmature code : ** immature code happens when learning how to use a new framework, library or tool. The first attempts are based on documentation examples, and their aim is to makes things works. Once it's working, it is worth cleaning, trimming or changing with another function learned along the way. Now, this is experience talking ! ` $sqlite = new Sqlite('db.sqlite') ; $query = " SELECT age FROM customer WHERE id = '$id' " ; $res = $sqlite->query($query) ; $row = $res->fetchArray() ; $age = $row[0] ; ` This may be simplified with : ` $sqlite = new Sqlite('db.sqlite') ; $query = "SELECT age FROM customer WHERE id = '$id' " ; $age = $sqlite->querySingle($query) ;` - **Debug code** : similar to the reasons that lead to Dead Code, some instructions are introduced in the code to report punctual status. This means `var_dump` and `print_r`, but it may also be extra `print` or `echo`, or even messages being logged. Depending on the debugging sessions, those messages may be very precise, and not worth keeping in production. - **Left for later** : some structures may be introduced in the code for later use. This is the case for classes, interfaces that are derived from another, but won't get any special code until the class can show some specific behaviour. As a first version, the default parent class is used. During review, I like to make sure those structures are not missing anything specific. `try/catch/finally` or `if (xxx_error()) { /* RFU*/ }` are both good places to check if anything was forgotten. It is one thing to stay focused on business logic, and another to review code and make it robust. - **PHP configuration** : any code depends on PHP configuration or status, there is a good chance this code might need review. `function_exists()`, `ini_get()` or `memory_limit()` functions call are good spots to investigate. For example, if the code needs to check for the existence of a PHP function, then it is an old PHP function or an upcoming one. At the next version update of the code, this code may be abandoned, for the good of everything. This list is short, so it is practical to remember and apply anytime I'm preparing some code to review. Except for the 'Left for later' code, all of them means that code will be removed, and the final result will be shorter. Over 1000 lines of code, I usually shave off 10 full lines of code. This is not much, but the feeling about the code quality is incredible. --- # PHP Types and Tests: Not Rivals, Just Working Different Shifts Source: https://www.exakat.io/php-types-and-tests-not-rivals-just-working-different-shifts/ # Types and Tests: Not Rivals, Just Working Different Shifts A common source of confusion in PHP development is the relationship between static analysis and unit testing. Are they redundant? Does adding types mean fewer tests? Does a comprehensive test suite make a type checker unnecessary? No to all three. They cover different grounds. The interesting question is where exactly the boundary lies, and what happens at the edges. In this blog post, we'll use a prime number checker as our canvas. Simple enough to hold in your head, rich enough to expose all three zones: what types cover, what tests cover, and the narrow overlap where they touch. ## Starting naive Here, there are no type declarations. From the static code analyser mago's perspective, the incoming argument `$n` is of type `mixed`: a string, null, an array, anything. The analyser can barely reason about it. From a testing perspective, you can still write tests for such values, but you're also responsible for testing inputs that should never have been passed in the first place. More importantly, both tools have to work harder to cover ground that neither should cover. And there are correctness bugs too: `isPrime(1)` returns `true`, `isPrime(0)` loops forever, the `$i < $n` bound is may be easily optimized with, at least, a `sqrt()`. ## The type system takes the first shift Our first step is to add types. It is not just courtesy, it's a contract: Run mago on callers: ``` ``` ``` ``` Three entire categories of wrong input, caught before the test suite even runs. You do not need tests for `isPrime("hello")`: the type checker owns that case permanently, at zero runtime cost. But here is where PHP's type system hits a hard ceiling: `int` represents a signed integer. There is no native `positive-int` type in PHP 8.x. `isPrime(-7)` passes the type check without complaint, even though a negative number as input to a primality test is semantically nonsensical. The type system has done everything it can. It cannot express value-level constraints like "greater than one" with the native scalar PHP types. It is worth nothing that static analysers may have a feature for that particular case, using PHPdoc types. We'll keep that on the side here, as it makes the explanations less clear. ## Tests pick up the slack When the type system hits its limits, it is where unit tests step in. A data provider covers the value-level cases that types cannot: The type `int` already guarantees these are integers. The tests guarantee they behave correctly *as values*. That is a different job entirely. `two` is worth calling out: it is the only even prime, and a naive loop starting at 2 immediately divides it evenly and returns `false`. A type checker has no way to know the algorithm is wrong for a specific value. Only a test can catch that. Any edge cases, or values that are close to limits or strange situations must be included in the tests. ## The overlap zone Look at the negative test case. When you write `isPrime(-7)` and assert `false`, you are making a statement that could have been a type constraint. If PHP had a native `positive-int` type, or if you add a PHPDoc annotation that mago understands: Then passing `-7` becomes a static analysis error, and the negative test case is now testing something the type system already forbids. The test is redundant from a type perspective: though you might keep it as behavioral documentation, or remove it as noise. This is the overlap: a narrow zone where a test and a type constraint are answering the same question. It is not large, but it is where the most interesting design decisions live. When you find yourself testing a value the type system could have forbidden, ask: should this be a type constraint instead? ## Large numbers: when int runs out PHP's `int` is 64 bits on modern platforms: `PHP_INT_MAX = 9223372036854775807`. For testing Mersenne primes or any serious number theory, that is nowhere near enough. Pass `PHP_INT_MAX + 1` to your function and PHP silently promotes to `float`, losing precision. The type declaration says `int`, but you are no longer sending integers: PHP now stops with a type error. One option is to move from `int` to `GMP`: `\GMP` is a proper object type. Mago can reason about it fully. The type system is back in business, and you can now test at arbitrary scale: But notice: `\GMP` is still a signed numeric type. `gmp_init(-7)` is perfectly valid. The negative case has not gone away: it has just moved to a different type. Tests still need to cover it. And, in particular, there is not more PHPdoc `/** @param positive-gmp $n */`, unless static analysis tools read this, and add it (and if they do, I'll rewrite the article with BCmath). Switching representation gives you back the type system. It does not eliminate value-level concerns. ## int|\GMP: union types as a complexity multiplier The natural instinct when refactoring an existing codebase is to accept both types and let callers choose: Mago tracks both branches of the union and warn you if you forget to handle one. That is real value. But look at the cost: every caller now has a decision to make. Every test case needs to decide which representation to use. The implementation carries an extra branch. And the negative case now manifests twice: once for `int` inputs and once for `\GMP` inputs. Union types increase expressiveness but transfer complexity to every call site. Sometimes the right call is to draw a hard boundary: accept only `\GMP`, and let callers convert with `gmp_init()`. The type becomes simpler, the implementation becomes simpler, and conversion happens at one explicit point. `int|\GMP` is not wrong. It is a deliberate tradeoff. The type system gained flexibility; the code gained surface area. That bargain is not always worth taking. ## The shape of the boundary | Concern | Type system | Tests | | ------- | ----------- | ----- | | Wrong type (string, null, float) | ✓ owned permanently | Not needed | | Algorithm correctness (`n=2`, `n=1`) | ✗ | ✓ | | Overflow / representation switch | Signals the problem | ✓ | Types are a permanent, zero-runtime-cost constraint on shape, or category of data. Tests are executable specifications on behavior. Neither replaces the other. The first question is to check if the types are covering exactly the domain values. Here, `int` are not, and, to some extend, `GMP` neither. That means tests have to step in and make sure anything left over by types are under control. Usually, this boils down to values, which more general behavior goes to types. The prime number checker was trivial, yet it turned out to be tricky. The same problem play out in every codebase. Every contraints that can be linked to the type, can be removed from the tests. Otherwise, it must go to the tests. And between tests and types, in the gray zone, sometimes, you need checks from everyone. --- # php-typos: a fast spellchecker for PHP codebases (Tool Review) Source: https://www.exakat.io/php-typos-a-fast-spellchecker-for-php-codebases-tool-review/ # php-typos: A Fast Spellchecker for PHP Codebases (Tool Review) There's a particular kind of embarrassment reserved for the typo that ships to production. A `recievePayment()` method. A config key spelled `treshold`. A comment that confidently explains the "lenght" of an array. None of it breaks anything, all of it makes the codebase look a little less cared-for, and most linters won't say a word about it. [php-typos](https://github.com/chr15k/php-typos) by chr15k sets out to catch exactly this class of problem. It's a source-code spe[![](https://www.exakat.io/wp-content/uploads/2026/06/header-dark-300x300.png)](https://www.exakat.io/wp-content/uploads/2026/06/header-dark.png)llchecker for PHP projects, and the headline pitch is speed: it's *fast*. Having spent some time with it, here's where it shines and where it still has room to grow. ## What it is php-typos is a Composer package that scans your PHP source for spelling mistakes in comments, strings, and identifiers, then optionally fixes them. Under the hood it's a wrapper around the excellent [`typos`](https://github.com/crate-ci/typos) Rust CLI from crate-ci, which is where the blistering performance comes from. The clever part is the packaging: the correct platform binary ships with the package, so there's no Rust toolchain, no Cargo, and no Homebrew to set up. You install it like any other dev dependency and run it the same way you'd run Pint or PHPStan. Getting started is genuinely a two-step affair: ``` composer require chr15k/php-typos --dev ./vendor/bin/typos --init ``` The `--init` step drops a `_typos.toml` into your project root, where you configure paths to exclude and words to ignore. From there, a bare `./vendor/bin/typos` scans the whole project, or you can point it at specific directories like `app/ tests/ config/`. It targets PHP 8.3+ and ships binaries for Linux (x86*64/ARM64), macOS (Intel and Apple Silicon), and Windows (x86*64). ## The good **It's genuinely fast.** This is the first thing you notice and the main reason to reach for it. Because the heavy lifting is done by a native Rust binary rather than PHP iterating over your files, scanning even a large project feels instant. That speed is what makes it viable to run on every commit or in CI without anyone grumbling about build times. **The fix workflow is well thought out.** A spellchecker that only complains is half a tool. php-typos gives you a `--write` flag that applies corrections directly to the affected files, and — importantly — a `--diff` flag that shows you a unified diff of what *would* change first. That review-before-you-commit step matters a lot here, for reasons I'll get to below. The two flags are mutually exclusive, which is the right call. **Output formats cover both humans and machines.** The `--format` flag gives you `long` (the default, with full file/line/correction detail), `brief` (one line per file), and `json`. The human-readable modes are pleasant to read, and the JSON output is the bit that makes this tool a good CI citizen — you can pipe it into annotation tooling so typos show up inline on pull requests rather than buried in a log. There are also handy introspection flags like `--files`, `--identifiers`, and `--words` for seeing exactly what the scanner is looking at. **It slots into existing pipelines cleanly.** Because it behaves like Pint or PHPStan, dropping it into a Composer `ci` script or a GitHub Actions step is trivial: ``` - name: Check for typos run: ./vendor/bin/typos --format=json ``` For a tool whose whole value proposition is "low friction," that consistency with the tools PHP developers already use is exactly right. ## The rough edges For all that, php-typos inherits some real limitations, and a couple are worth weighing before you adopt it. **English only.** This is the big one, and it comes straight from the underlying `typos` engine. The Rust tool is built around a curated list of English typo-to-correction mappings rather than a swappable dictionary, and its `locale` option only switches between English dialects — not between languages. If your codebase has German, French, or Spanish comments, identifiers, or domain terms, php-typos has nothing to offer them and will likely flag perfectly correct words as mistakes. Multi-language support would dramatically widen who this tool is useful for, and right now it simply isn't there. **Weak handling of composed words.** Related to the above, support for compound and domain-specific words is thin. Code is full of conjoined terms and jargon that aren't "real" dictionary words, and a spellchecker that understood common coding conventions — or could be taught your project's vocabulary more gracefully than a flat ignore-list — would produce far fewer false positives. You *can*whitelist words in `_typos.toml`, but it's a blunt instrument compared to genuine awareness of how developers compose identifiers. **No understanding of PHP's structure.** This is the limitation I'd want every adopter to internalize. php-typos treats your code as text; it doesn't know the difference between a typo in a comment and a typo in a class name. Fixing a misspelled word in a comment is harmless. Auto-fixing a misspelled *property name*, *method name*, or *class name* is another matter entirely — those are part of your public surface and your runtime behaviour. A rename that `--write` happily applies could break a magic method, a serialized property, a reflection-based lookup, an API contract, or a dozen other things that a pure text tool can't see. The `--diff` flag is your friend here, and I'd treat blind `--write` on identifiers as something to do consciously rather than reflexively. A PHP-aware version that distinguished "safe to auto-fix" comment text from "needs human judgement" identifiers would be a substantial step up. **It's a thin wrapper.** None of the above is a knock on the author's effort — packaging a cross-platform binary so it "just works" via Composer is a genuinely nice piece of developer experience. But it does mean php-typos is fundamentally a convenience layer over someone else's binary. A more idiomatic, PHP-native implementation — one that could parse PHP properly, reason about identifiers versus comments, and grow language and compound-word support on its own terms — would be a more powerful and more maintainable foundation in the long run. It's also worth noting this is an early-stage project (it's still in the 0.x versions, with a small but active release history), so some of these gaps are exactly what you'd expect from a young tool finding its feet. ## The verdict php-typos is a nice, fast tool that does a real job well. If you have an English-language PHP codebase and you want to stop shipping typos in your comments and strings, it's an easy win: quick to install, quick to run, friendly in CI, and equipped with a sensible fix-and-review workflow. For that use case, it earns its place alongside Pint and PHPStan in a project's quality toolchain. The caveats are about ambition rather than execution. English-only support, limited handling of composed words, and a lack of PHP-structural awareness all cap how far you'd want to trust it — particularly the auto-fixer on anything that isn't plain prose. Lean on `--diff`, keep `--write` for the safe stuff, and you'll get a lot of value out of it today. Where I'd love to see it go: multi-language support, smarter handling of coding conventions and compound words, and ideally a more idiomatic PHP-native core that understands the code it's checking rather than treating it as a wall of text. The bones are good. There's room for this to become something more than a fast wrapper. --- # Putting PHP’s M_EULER to Work Source: https://www.exakat.io/putting-phps-m_euler-to-work/ # Putting PHP's M_EULER to Work PHP ships with more mathematical constants than most developers ever use. One of the most overlooked is `M_EULER`, which is the Euler constant. So, before putting PHP's `M_EULER` to work, we need to clear up a confusion with another Euler constant in PHP: `M_E`. ## M_E vs M_EULER: Two Constants, Two Different Things Open the PHP manual and you will find both `M_E` and `M_EULER` sitting near each other in the list of predefined math constants. They are not the same number, in PHP as in the mathematical world, and they do not describe the same concept. **`M_E`** is Euler's number, the base of the natural logarithm: ``` M_E ≈ 2.718281828459045 var_dump(exp(1) == M_E); // Never compare floats, but in this case, PHP recognize them as equals! ``` You use `M_E` whenever you need the exponential function base: compound interest, population growth, `exp($x)` calculations, and so on. It is named after Leonhard Euler, but it was actually introduced by Jacob Bernoulli and later systematised by Euler, which is why it also carries his name informally. **`M_EULER`** is the Euler–Mascheroni constant, sometimes written γ, or gamma: ``` M_EULER ≈ 0.5772156649015329 ``` This is a completely different beast. It emerges from the gap between the harmonic series and the natural logarithm: ``` γ = lim(n→∞) [ (1 + 1/2 + 1/3 + ... + 1/n) − ln(n) ] ``` `M_EULER` describes how quickly the harmonic series diverges relative to the logarithm. It appears in number theory, probability, combinatorics, and, crucially in this post, in the running tests problem, also known as the coupon collector's problem, which is exactly where we are headed. ``` ``` Keep that distinction in mind. Everything that follows uses `M_EULER`, not `M_E`. ## The Coupon Collector's Problem: How Many Tests to Cover The Source? Imagine you have a function with unit tests that internally branches into n distinct code paths. You are running randomised tests: each test exercises one path chosen uniformly at random. This is the systematic approach, where you know there are a number of distinct paths to walk. Now, imagine that you have a set of n tests, and you want to run a random subset of the tests to get a preview of the situation. Later, obviously, you will run all of them, but right now, you want to catch the obvious errors, if any. How many tests do you need to run individually before you have hit every path at least once? This is the classic coupon collector's problem, and its expected value has an elegant closed form: ``` E[T] = n · H(n) where H(n) = 1 + 1/2 + 1/3 + ... + 1/n ``` For large n, the harmonic number H(n) is approximated by: ``` H(n) ≈ ln(n) + γ ``` So the expected number of random tests required to cover all n paths is: ``` E[T] ≈ n · (ln(n) + M_EULER) ``` Here is a clean PHP function for this: ``` = 1.0) { throw new \InvalidArgumentException('target must be strictly between 0 and 1.'); } $p = log(1.0 - $target) / log(1.0 - 1.0 / $n); return (int) ceil($p); } /** * Express p as a fraction of the full-coverage baseline (which uses M_EULER). */ function coverageRatio(int $n, int $p): float { return $p / expectedTestsForFullCoverage($n); } // For n = 200 paths, how many tests for various coverage targets? $n = 200; echo "Target coverage for n = $n paths\n"; echo str_repeat('-', 55) . "\n"; printf("%-10s %-15s %-25s\n", "Target", "Tests needed", "Fraction of full baseline"); echo str_repeat('-', 55) . "\n"; foreach ([0.50, 0.75, 0.90, 0.95, 0.99] as $target) { $p = testsForTargetCoverage($n, $target); $ratio = coverageRatio($n, $p); printf("%-10s %-15d %.2f×\n", round($target * 100) . '%', $p, $ratio); } ``` **Output:** ``` Target coverage for n = 200 paths ------------------------------------------------------- Target Tests needed Fraction of full baseline ------------------------------------------------------- 50% 139 0.09× 75% 277 0.18× 90% 460 0.30× 95% 598 0.39× 99% 918 0.60× ``` A few things stand out from these numbers: - Covering half your paths takes only about 9% of the budget you would need for full coverage. - Getting from 90% to 99% nearly doubles the test count: it is a tradition that the last few percents are disproportionately expensive. - The full-coverage baseline, expressed via `M_EULER`, serves as a natural unit of measurement. Saying "we spend 0.30× the baseline for 90% coverage" communicates far more than a raw number. ## Putting It All Together Here is a compact summary function that outputs all three insights at once, useful for a CI planning script or a test-suite sizing tool: ``` **When it shines**: reverse generation is most useful when a regex validates input before processing. You want to prove every string validation passes is also a string your processing code handles gracefully: no silent truncation, no type errors, no format assumptions baked into the parser. Just plain reality check. ## Reverse regex: what it is and how to install it A reverse regex generator takes a pattern and produces strings guaranteed to match. Instead of checking a string against a pattern, it walks the pattern's parse tree, makes random choices at every branch and quantifier, and assembles valid output. We'll use `pointybeard/reverse-regex`, a maintained fork of `icomefromthenet/ReverseRegex`. It parses PCRE patterns into a generator tree with a pluggable random source. The pipeline: lexer → token stream → parser → generator tree → `generate()` with random source. You don't need to care about the internals to use it, but it's nice to know what's happening. ### Install ``` composer require pointybeard/reverse-regex ``` The tool requires PHP 7.2+. It works without a warning until PHP 8.3, then starts running into deprecations, but it keeps working fine. Load the autoloader and import the four classes: ### The basic pattern Every generation call follows the same structure: > Unlike preg_match(), this library doesn't use PCRE delimiters. Pass the bare pattern: no slashes or any other alpha-numeric delimiter, and no flags. ## Examples: simple to complex In the next examles, I'll reuse the same boilerplate: just plug in the pattern and run it. ### Fixed literal A fixed string always generates itself. Boring but it confirms the setup works. ### Character class with exact repetition A slug or token pattern: fixed-length lowercase alphanumeric. ### Range quantifier `{3,8}` means the library picks a random length between 3 and 8 on each call. Good for exercising code that handles variable-length input. ### Digit shorthands `\d` expands to `[0-9]`. Good for numeric formats. Notice `0719-88-54` matches the pattern — four digits, dash, two digits, dash, two digits — but month 88 doesn't exist. The regex describes a format, not real calendar dates. This is exactly the kind of edge case reverse testing surfaces. ### Alternation `|` picks one branch at random. Run the generator lots of times to hit every branch. ### Groups with alternation and quantifiers Nest groups, quantifiers, and alternation for structured output. ### Structured format — product SKU Combine everything into realistic values: a SKU like `CAT-XXXX-###`. ### Structured format — IPv4 address The generator produces valid-looking addresses, including ones outside the 0–255 range. Format validation only gets you so far. ### Full fuzz test harness A reusable helper that generates strings from a pattern and runs a callable assertion against each. Drop this in your test suite. ### Reproducibility with a seeded random `SimpleRandom` is non-deterministic. When a fuzz run finds a failure, you'll want to replay it. Pin the seed with `srand()`: PHPUnit tip: log the seed at the start of each test run. CI flags a failure → re-run with that seed → identical strings → reproducible bug. ## Limits of reverse regex It's a good tool. It's not magic. Here are some aspects to keep in mind when using it. ### Volume matters One generated string tells you almost nothing. A pattern with ten character classes, each with 62 possibilities, is an astronomical search space. Budget 1,000+ iterations in unit tests, 10,000+ in dedicated fuzz runs. ### Nobody wants to read generated strings Someone called `Xqmpklzt` or a licence plate `VB-047-RMQZ`are structurally correct and semantically garbage. Great for testing, terrible for failure messages, demo databases, or anything a human has to read. If you need to test these or seed databases, it might be better to rely on Faker, for more realistic values. ### Unbounded quantifiers are dangerous `*` and `+` have no upper bound. The library interprets them as `PHP_INT_MAX` max repetitions. In practice it usually doesn't go crazy, but `\w+` could theoretically produce a million-character string. Use `{n,m}` ranges with sane limits. It is safer for generating data, but also for the validation regex. ### Format ≠ semantics The generator guarantees structural matches. It can't enforce month ≤ 12, IP octets ≤ 255, valid IBAN checksums, or deliverable email addresses. If your validation relies on rules not encoded in the regex, the generated output only probes the structural layer. ### Unsupported PCRE features Lookaheads `(?=...)`, lookbehinds `(?<=...)`, backreferences `\1`, named captures `(?P...)`, and Unicode `\p{...}` aren't supported. Test your pattern in isolation before wiring it into a fuzz harness. ### No negative testing Reverse generation only produces strings that match. For near-misses, off-by-one, or single-character-outside-class cases, you still write those by hand or use a mutation-based approach. ## Testing regex with reverse generation: use it, don't rely on it Reverse regex is one layer. Combine it with hand-written edge cases for semantics, property-based testing libraries for wider mutation, and integration tests against real-world data. No single technique covers everything. Together they make your regex much harder to fool. --- # The Forbidden PHP Features Encyclopedia Source: https://www.exakat.io/the-forbidden-php-features-encyclopedia/ # The Forbidden PHP Features Encyclopedia Within the vast realm of PHP development, certain features have incurred the disapproval of various developers, leading to their outright exclusion from conscientious coding practices. This comprehensive encyclopedia serves as a curated catalog, shedding light on the forbidden features that have faced scrutiny and are best avoided in the pursuit of secure, maintainable, and efficient PHP code. Explore this authoritative guide to safeguard your projects and adopt best practices in the ever-evolving landscape of PHP development. Here are things that you should not use in your code. The reasons are behind the links. - **array_merge()** [Never Use array_merge() in a loop](https://dev.to/chemaclass/never-use-arraymerge-in-a-loop-3mj0) - ** compact()** [You'll Never Believe What One Developer Did With compact()](https://radcampaign.com/blog/youll-never-believe-what-one-developer-did-compact) - [PHP's "compact" - Pros and cons](https://laracasts.com/discuss/channels/general-discussion/phps-compact-pros-and-cons) - [`compact()` function in PHP, and why it is problematic due to its magic behavior](https://gist.github.com/Ocramius/c56a8e8ff25a8e0bd96800c41edab02a) - [PHP 8.1: Warning on `compact` function calls with non-string and non-array string parameters](https://php.watch/versions/8.1/compact-non-string-warning) - **boolean as arguments** [The danger of boolean flags in object methods](https://sarah-savage.com/the-danger-of-boolean-flags-in-object-methods/) - **closing tags** [Should You Close PHP Tags? The Debate Continues...](https://www.sitepoint.com/closing-php-tags-debate/) - **else** [Avoid using else ](https://freek.dev/2212-avoid-using-else) - [How to simply avoid “else” in PHP and other programming languages](https://thomas-sanctorum.medium.com/how-to-simply-avoid-else-in-php-and-others-programming-language-ccd6a07c4e1) - **empty()** [When to use empty in PHP? I'd say never](https://www.beberlei.de/post/when_to_use_empty_in_php_i_say_never) - [Using isset() and empty() hurts your code ](https://dev.to/aleksikauppila/using-isset-and-empty-hurts-your-code-aaa) - [Avoiding empty() in PHP](https://localheinz.com/articles/2023/05/10/avoiding-empty-in-php/) - [Be Careful Using PHP's empty()](https://www.zachstronaut.com/posts/2009/02/09/careful-with-php-empty.html) - [Never use the empty function](https://dev.to/klnjmm/never-use-empty-function-in-php-4pb0) - **eval()** [When is eval evil in php?](https://stackoverflow.com/questions/951373/when-is-eval-evil-in-php) - [Eval Is Evil: Analyzing Performance of Web Applications Based on PHP and JavaScript by Static Analysis](https://link.springer.com/chapter/10.1007/978-981-10-3935-5_12) - [The Delicious Evils of PHP](https://www.sitepoint.com/the-delicious-evils-of-php/) - [The Danger Of PHP Eval()](https://medium.com/%40pkhuyar/the-danger-of-php-eval-a23410187ca2) - [The land where PHP uses Eval()](https://www.exakat.io/land-where-php-uses-eval/) - [What you should know about PHP security](https://snyk.io/fr/blog/php-code-security/) - [Massive abuse of abandonned Eval PHP wordpress plugin](https://blog.sucuri.net/2023/04/massive-abuse-of-abandoned-evalphp-wordpress-plugin.html) - [7 schocking dangers of PHP eval()](https://hyscaler.com/insights/7-shocking-dangers-of-php-eval-function/) - [PHP code injections and 4 prevention tips](https://brightsec.com/blog/code-injection-php/) - [Why you should always avoid PHP eval() function](https://qubitsandbytes.co.uk/php/why-you-should-avoid-phps-eval-function/) - **extends** [Inheritance vs Composition: Composition wins by knockout](https://r.je/you-do-not-need-inheritance-oop) - [Stop Using “extends” in PHP](https://blog.devgenius.io/stop-using-extends-in-php-37c9da1cce83) - [Are Traits and Inheritance antipatterns in PHP?](https://zero1-dev.medium.com/are-traits-and-inheritance-antipatterns-in-php-64c67f97011a) - **extract()** [PHP bad practice: the use of extract()](https://dzone.com/articles/php-bad-practice-use-extract) - **float** [No Floaters](https://github.com/Roave/no-floaters) - **global** [Are global variables considered bad practice in PHP?](https://www.quora.com/Are-global-variables-considered-bad-practice-in-PHP) - **goto** [GOTO in PHP 5.3: is it Really That Evil?](https://www.phparch.com/2009/06/goto-in-php-5-3-is-it-really-that-evil/) - **Interfaces** [Object interfaces are evil](https://tonymarston.net/php-mysql/object-interfaces-are-evil.html) - [Interface objects may only include hooked properties](https://peterbabic.dev/blog/interfaces-may-only-include-hooked-properties/) - [Please Do Not Interface the PHP World](https://hermanradtke.com/2011/10/22/please-do-not-interface-the-php-world.html/) - [Interfaces in PHP don't make complete sense](https://medium.com/@maxalmonte14/interfaces-in-php-dont-make-complete-sense-4e63be8e4233) - **Named parameters** [Opting-out of named parameters](https://php.watch/articles/no-named-arguments-docblock-attribute) - **Promoted properties** [Why I dislike the PHP 8 Constructor Property Promotion in the current state](https://pegasusgate.net/+bob/35/why-i-dislike-the-php-8-constructor-property-promotion-in-the-current-state) - **Runtime checks** [We don't need runtime type checks](https://stitcher.io/blog/we-dont-need-runtime-type-checks) - **strict_types** [Why use declare(strict_types=1) in PHP – Fast tips](https://inspector.dev/why-use-declarestrict_types1-in-php-fast-tips/) - **Singleton pattern** [Don't use a Singleton pattern](https://github.com/piotrplenik/clean-code-php#dont-use-a-singleton-pattern) - [Stop Using “Singleton” Pattern](https://medium.com/@dotcom.software/stop-using-singleton-pattern-c078abc99eb2) - [The truth about the truth about static and singletons](https://dev.to/jmau111/the-truth-about-the-thruth-about-static-and-singletons-lfe) - **Static methods** [Don't Use Service Classes with Static Methods ](https://dev.to/clintwinter/dont-use-service-classes-with-static-methods-1ebo) - [When Should You (And Shouldn’t You) Use Static Methods in Laravel/PHP? A Practical Guide.](https://medium.com/@laravelprotips/when-should-you-and-shouldnt-you-use-static-methods-in-laravel-php-a-practical-guide-1b1caac46f2d) - **String with variables** [Prefer string interpolation over concatentation in PHP](https://roman-huliak.medium.com/prefer-string-interpolation-over-concatenation-in-php-b6c3c651deda) - [String interpolation in PHP](https://jeffreyeverhart.com/2019/11/21/string-interpolation-in-php/) - [Learning PHP: Concatenate Strings and Variables Efficiently](https://wpshout.com/php-concatenate-strings/) -  **Traits** [My Love / Hate Relationship with PHP Traits](https://www.coderslexicon.com/my-love-hate-relationship-with-php-traits/) - [Are Traits and Inheritance antipatterns in PHP?](https://zero1-dev.medium.com/are-traits-and-inheritance-antipatterns-in-php-64c67f97011a) - [Why I Avoid PHP Traits (And What I Use Instead)](https://dev.to/tegos/why-i-avoid-php-traits-and-what-i-use-instead-1288?ref=dailydev) - **array type** [never type hint on arrays](https://steemit.com/php/@crell/php-never-type-hint-on-arrays) - **Type checks** [We don't need execution type checks](https://stitcher.io/blog/we-dont-need-runtime-type-checks) - **One letter variables** [Is there a time and place for single letter variable](https://chuniversiteit.nl/papers/single-letter-variables) - **Variable variables** [The Dangers of PHP's $$](https://andycarter.dev/blog/the-dangers-of-php-variable-variables) --- # The Empty String in PHP: One Value, Too Many Jobs Source: https://www.exakat.io/the-empty-string-in-php-one-value-too-many-jobs/ # The Empty String in PHP: One Value, Too Many Jobs The humble empty string "" is everywhere. It's the quiet default, the silent failure signal or the silent success signal, the lazy cast, the natural accumulator. Behind the obvious simplicity of the empty string in PHP lies a surprising amount of overloaded meanings, which leads to misunderstandings. Let's review some situations. ## Default Value for Properties and Arguments The most common use of "" is perfectly reasonable: signalling "nothing provided yet." As a default, the empty string is idiomatic, type safe, fatal error free and generally fine: it provides a "no value" meaning, and "empty string" is genuinely the same thing in so many domains. Yet, a missing name and a name that was explicitly cleared are two different states. When that distinction matters, many application rely on ?string with a null default instead: Now, there is no more confusion between no value and empty string. This is at odds with PHP trying to enforce using string with native functions, rather than accept null as the empty string. Such tradition is probably at the root of many modern errors. ## Invalid Value: Where Even Empty String Can't Go ord() has no problem with default values: its only argument is actually compulsory. It is a string. But what happens when an string is passed to ord()? it returns the ASCII number of that character. String, character? The slip in the vocabulary is important. ord() returns the ASCII number of the first character in the passed string. PHP has no way to specify a single character, and the best type is to use string. Since ord() only works on the first element of the string, it emits a warning since PHP 8.5 to indicate that the string must be of length 1. And it emits the same warning for a string of length 0, such as the empty string. At that point, the warning is not about the unused information, but rather the absence of character to process: an empty string has no character. There are some situations where an empty string should not be allowed, because, while it is a string, it is also holding no character. So, sorting an array of strings, or looking for the minimum or maximum in that same array might be subject to adaptation to a specific domain. By default, with PHP, empty string has the lowest precedence over any other string. ## Error Sentinel: Nothing Could Be Processed Several built-in PHP functions, and quite some custom ones, return "" to signal failure. substr() is a classic example: in pre-PHP 8.0, it returned false in case of failure. It would also return "" when asked for it, like when the $length parameter is passed as 0. This pattern also appears in userland code: The problem is that "" is a valid, successful result in many contexts. Returning it as an error conflates two meanings. The caller now has to guess: The better approach is explicit: throw an exception, return null, or use a result type. Reserve "" for "a string that happens to be empty," not "something went wrong". All considered, it is the same as the default value we met in the first phase, but now, at the end of the process, not the beginning. ## Success Sentinel: Non-Empty Means Error And now, this: the inverse pattern, and arguably the most puzzling one for the non-initiated. A function returns "" on success and a non-empty message string on failure: This shows up in older codebases, some validation libraries and system calls. When it works, there is nothing to report, so the returned value is "". When something fails, there is a concrete error message, and the returned string will be not-empty. This approach works, but it inverts the normal semantics of a return value. Reading if ($error !== '') is a small cognitive stumble every time. A bool return with a separate error retrieval mechanism, a thrown exception, or a proper validation result object communicates intent far more clearly. ## The Empty-String Cast PHP's loose typing makes it easy to use "" as an implicit cast to string: Or worse, initialising a variable as "" so a subsequent concatenation typecasts whatever ends up in it: This is fragile, prone to Fatal Error, when used with object, or to Warnings, when used with arrays. It depends on implicit coercion and __toString magic, hides the intent, and produces confusing bugs when objects don't implement __toString. Use an explicit cast: This is all the more strange that there are quite some situations where it works without warning. It is the case with integers, null, booleans... The short cut is tempting, until it becomes important to check what happened to the concatenated value. At that point, explicit is better than implicit. ## String Accumulator Initialisation This final situation is perhaps the most widespread use of "", and the one most worth replacing: Starting with "" and appending in a loop, or over many method calls, is intuitive but subtly inefficient: PHP extends the string everytime one extra piece is added to the accumulator variable. Most of the time, it is quick, as PHP pre-allocate extra memory for such an event, but at some point, it has to stop and, extends the memory or copy the new string somewhere with more space. The idiomatic PHP replacement is to collect the bits and pieces into an array and implode() it all onace, at the end: This is cleaner, memory efficient and faster. It makes the separator handling obvious, even if the separator is the empty string "" itself. ## So many usage of the empty string in PHP The empty string is a nice feature, although it is not even considered as such: it comes with the language, and anyone would look for it if it was missing. But it is so common that tt's just been handed far too many jobs. The fix, in almost every case, is the same: disambiguate the empty string and be explicit about what it means. PHP has the tools, may it be null, typed returns, exceptions, arrays, to code precisely the intend, without hiding it behind a pair of empty quotes. --- # strpos() Syndrom: When 0 And false Are Not The Same Source: https://www.exakat.io/strpos-syndrom-when-0-and-false-are-not-the-same/ # strpos() Syndrom: When 0 And false Are Not The Same In PHP, several functions can return **`0`** or **`false`** or even **null**, and all these values may be confused for different things. The most popular function with this syndrom is `strpos()`, hence the name of this classic PHP bug. `strpos()` returns the position where the string was found. In the case of `b` below, it returns `1`. And `1` is truthy. When `strpos()` does not find the requested string, as it is the case for `d`, then it returns `false`. The problem arise when `strpos()` finds the requested string on the first position of the haystack. Then, the returned position is 0, which is falsy. At this point, `string not found` and `string found at position 0` are confused, and it leads to a bug. In this case, an error is converted to a `false` value, while a valid return value may also be confused with `false`, such as 0, `''` (empty string), `false`, `[]` (empty array) or `null`. Nowadays, PHP tends to throw exceptions to avoid confusing one for the other, such as with `json_decode()`, though some previous behaviors are still in place. There are some notable PHP functions which produce this behavior: ### String Functions - **`strpos()`** – Returns the position (0 or higher) or `false` if not found. - **`stripos()`** – Case-insensitive version of `strpos()`. - **`strrpos()`** – Returns the position of the last occurrence or `false`. - **`strripos()`** – Case-insensitive version of `strrpos()`. - **`strstr()`** – Returns the substring or `false` if not found. - **`stristr()`** – Case-insensitive version of `strstr()`. - **`strpbrk()`** – Returns the substring or `false` if not found. --- ### Array Functions - **`array_search()`** – Returns the key (which can be `0`) or `false` if not found. --- ### Other Functions - **`preg_match()`** – Returns `1` for a match, `0` for no match, or `false` on error. - **`preg_match_all()`** – Returns the number of matches (which can be `0`) or `false` on error. - **`json_decode()`** – May return valid decoded `false`, 0 or `null` values. ## Best Practices ### Strict comparison One solution to protect one's code against this problem is to always use a strict comparison, with the `===` or `!==` operators. )** when checking the return value of these functions to avoid confusion between `0` and `false`. Example: ``` ``` ### Use safe functions Another solution is to use dedicated functions, such as `str_contains()`, `str_start_with()`or `str_end_with()`, instead of `strpos()`. There isn't always a straightforward replacement for the orginal function, such as for `json_decode()`, or `preg_match()`. In that case, you should create it yourself, with the added context. Example: ``` ``` ### Use static analysis Static analysis tools, like [Exakat](https://www.exakat.io/), are able to detect this error and focus attention on them before the code goes to production. ## Explicit comparison The `strpos()` syndrom is not limited to that function: it comes also with `array_search()`, and others. It usually takes a few hours of head scratching to find the error and make the code work again. This is a [PHP classic bug](https://php-dictionary.readthedocs.io/en/latest/thesaurus.html#classic-bug) that should be known to every developer that work with the platform.   --- # Usages of PHP Static variables Source: https://www.exakat.io/usages-of-php-static-variables/ # Usages of PHP Static variables I thought static variables were a lesser known PHP feature. Then, I counted about 30% of PHP projects using it, and I realized it was a more common feature than I thought. If you haven't encountered then, they are distinct from static properties. Within a method, it is possible to declare a static variable. Static variables are kept alive between two calls of the method, allowing continuation between multiple calls. At first glance, static variables can act as a simple cache system: store something in it on the first call, and then, reuse it later. And, for the most part, this is their actual use. On the other hand, such a general description doesn't give an accurate view of the various cases addressed by static variables. The underlying reasons vary widely, and they are often not characterized as a simple cache. So, let's cover a number of them, to understand why almost 30% of PHP code use and love static variables. ## Memoization The first and direct application of static variables is memoization. It is a cache, that prevents recomputing the same value multiple times. Since a static variable is available across multiple calls, it is possible to avoid speed up multiple requests. The advantage of static variables is obvious here: the set up is very simple, and the value is then available everytime the method is called. In terms of memory, the variable is only allocated when the method is called the first time. This means that when the method is not used, no memory is used. This compares to setting the same cache in a property, static or not, within the class: a property is allocated at object creation, and does not require any usage. ## Lazy Singleton Inside a Function This situation is not a cache, but it happens when the function requires a service, which has to be initialized before usage. Here, the default value of the static variable helps triggering the early configuration. Later, the service is used without rebuilding it, saving a lot of processing. In this situation, the function must be autonomous in its fetching the service. Dependency injection is usually prefered, as it gives more control over the service, in particular for testing purposes. ## Complex initialization Before PHP 8.1, it was not possible to call another method to process the initialization of the variables. Hence, one had to rely on a default variable to detected this first call and act upon it. Note that such application changed since PHP 8.1. Static variables, which are variables, unlike static properties, accept any expression. That is the same as splitting the definition of the static variables in two expression: one for the static, one for the definition. On the other hand, class properties require a constant expression, as they can't handle variables nor functioncalls. ## A special first usage of the method We just saw that methods may need to set up a service upon the first call. This time, the first usage of the method is just a special case, which requires some special behavior. It may be more than a service fetch. Basically, the first call is handled differently. More complex scenarii include skipping the n first calls before activating some behavior. The static variable allows for storing state across multiple calls. ## Recursive Function State Beyond memoization, the variable content may actually changes between two calls. This is the case of recursive functions. These functions need to keep a counter, whose value change at every nested call. With a static variables, the depth is entirely hidden inside the function, no need to pass then around. Here, the static variable may be replaced by an extra parameter and a default value. This gives some control over the initialization of the recursion. ## Avoiding Recursive Infinite Loops This case is the opposite of the previous one: the static variable acts as a guard flag, that prevents reentry. This means that the function can be used once, but cannot be used again while being already in use. ## Single-call methods We'll finish by going from infinite loop to single call. As much as methods are supposed to be called when needed, sometimes they have to be constrained and limited to one call. There, the static method allows for such level of control. This may also be done with a global variable, so that other parts of the application may check if the initialization has been done or not. And, since anyone may update the global variable, it feels safer to avoid them. Static variable keeps it private to the function. ## Important Caveats Besides their clear advantages, static variables tend to have some limitations that modern PHP applictions avoid as much as possible: - Static variables are not testable: static state persists across calls, making unit tests pollute one another, unless when they are run in isolation. - Static variables are great for request‑only lifetime: they reset after the HTTP request ends. They may become a burden on long run application, like CLI or frankenPHP server applications. - Static variables are hard to debug: they are private to a method, and there is no way to observe them - Static variables may be replaced by clearer alternatives: they may be replaced by a property, a static property, an argument by reference or a global variables. In all cases, they become accessible from the outside. The good news is that the refactorisation is not too hard. ## When Are Static ariables A Good Choice? Static variables are a good fit for small scripts, short lived run, or throw away script. Either it runs, and restarts, often, or it is not used after a short amount of time. They are good when the covered code itself is small, and battle tested. This applies the philosophy: "If it ain't broken, don't fix it". For example, when debugging this piece of code is never considered, it is good to keep all its internals under wrap, including static variables. Rarely, static variables are good to same some memory. This one is usually harder to justify. ## Static Variables and their usage Beyond simple memoization and cache, static variables provide a quick tool to set up a local value that survives between function calls. They may be easily replaced by all almost all other data container: argument, properties, static properties, global variables. Since the refactor is rather easy, it is not a major problem. In fact, one last argument against them is that moving a variable to a property allows it to be typed. Static variables are not typed, and they may even change type during their lifetime. This is nice for a cache, but not so nice in other situations. --- # The Art of Being Anonymous in PHP Source: https://www.exakat.io/the-art-of-being-anonymous-in-php/ # The Art of Being Anonymous in PHP This post is a tour of PHP's nameless constructs: from closures and arrow functions to anonymous classes and non-binding catch blocks. Every day, in most PHP code, things have names. Variables are named, classes have identifiers, functions are called by their name. Though, PHP also offers a rich set of anonymous structures: constructs that exist and operate without ever being bound to a permanent name. Knowing when and why to use them will make the code more expressive, more flexible, and sometimes delightfully terse. Let's walk through every anonymous structure PHP offers, to understand their mechanics, and see where they may be put into action. ## Anonymous Classes PHP 7.0 brought anonymous classes: classes without a declared name. They are defined with `new class { ... }` and can implement interfaces, extend base classes, and accept constructor arguments, all inline. Just like named classes. Anonymous classes shine in two scenarios. First, in tests, when one need a lightweight stub or mock that satisfies an interface without a dedicated test-double file. Second, in application code when a named class would be used in a very localised context and naming it would add noise without adding clarity. ### Multiple anonymous objects That said, "anonymous" refers to the declaration, not the lifetime of the class. Once it has been instantiated, it is possible to re-instantiate the same anonymous class as many times as one like via new $object: The class has no name to be typed, but its definition is reachable through any instance of it. The anonymous part is the declaration: not the lifetime of the class itself. PHP internally assigns anonymous classes a generated name for reflection and error-reporting purposes. Don't rely on that name in production code: it's an implementation detail that can change. ## Anonymous Catch Blocks PHP 8.0 quietly introduced one of the nicest quality-of-life features in the language: the ability to catch an exception without binding it to a variable. When the code only need to handle the exception type and don't need to inspect the exception object itself, the variable is just noise. It may be dropped, just like that. The anonymous catch block is a small feature with a meaningful signal: by omitting the variable, you're explicitly telling readers "I know an exception occurred, I've categorised it by type, and I don't need its message or trace to handle this." It's self-documenting code. ## Anonymous Functions PHP offers two distinct flavours of anonymous functions. They share the "no name" characteristics but differ in how they capture their environment and what they can express. ### Closures A closure is the classic anonymous function. It is an expression that evaluates to a Closure object and can be stored in a variable, passed as an argument, or returned from another function. The key feature of a closure is explicit capture: variables from the enclosing scope are not automatically visible inside. They must be listed in the `use` clause. By default they are captured by value; prepend `&` to capture by reference. Closures are objects of the built-in Closure class. This means the object can call methods like bindTo() or call() on them to rebind `$this`: a powerful trick for meta-programming. ### Arrow Functions Introduced in PHP 7.4, arrow functions are a more concise syntax for closures with one key difference: they automatically capture variables from the outer scope by value, without an explicit use clause. In a sense they are just renamed closures with a shorter body: the same anonymous function concept, trimmed down. Arrow functions are limited to a single expression: no multi-statement body. This makes them perfect for short transformations and callbacks, but closures remain the right tool to handover a block of logic. Use an arrow function for one-liners passed to `array_map()`, `array_filter()`, or similar. Use a closure for multiple statements, for reference handling, or `$this` rebinding. ## Anonymous Methods via __invoke() Sometimes a full closure feels like too little: an object that behaves like a callable is much better. That's where PHP's magic method `__invoke()` comes in. Any class that defines it can be called like a function: making the invocation itself effectively anonymous from the caller's perspective. The method itself doesn't have a name that callers need to know; they simply invoke the object. This pattern is especially useful in frameworks expecting a callable: the full power of a class, with dependency injection, internal state, testability, interface implementations, while presenting a clean, function-like interface. ## Anonymous Constants This one is the most subtle, but worth naming explicitly. A literal value used directly in code is, in essence, an anonymous constant. And note that this applies to global and local constants only, not class constants, which are always tied to a class name. A bare literal has no identifier; it simply is the value. The concept matters because it draws the contrast with named constants. Literals are immediate, context-bound values. When a literal appears more than once or carries non-obvious semantics, it's a signal to give it a name to graduate it from anonymous to named. This is the well-known magic number anti-pattern in reverse: understanding why we name constants helps us appreciate what anonymity means for a value. ## What Must Stay Named Understanding the anonymous side of PHP is sharpened by understanding its limits. Anonymous form does not apply to some structures in PHP: some of them always have a declared name. - Variables: a variable is its name. The variable's value change overtime, so it cannot replace the variable. - Properties: one access properties by name. It is the same as variables, and no PHP property, such as hooks or magic method, allows to reach them without a name. - Traits: traits are pulled in via `use TraitName`: static code needs a name as reference. - Interfaces: same as traits, via `implements`and `extends`. - Enums: enumerations represent a fixed, named set of cases: anonymity would defeat the purpose. The pattern is consistent: any structure that must be referred to by name elsewhere in the codebase has no anonymous version. You can't implement a nameless interface, use a nameless trait, or read a nameless property. The name is not incidental: it is the mechanism. ## Putting It All Together I couldn'nt resist finishing with a pot-pourri of several anonymous structures can work in concert in a real-world scenario: a simple pipeline that processes a list of items: In just a few lines, we've used an anonymous class, a closure with `$this`, an arrow function, and a non-capturing catch. Each anonymous structure earns its place: the class won't be reused elsewhere, the callbacks are short-lived transformations, and the exception's details don't matter to the recovery logic. ## Anonymous structures in PHP PHP's anonymous structures are more than a convenience syntax: they're a design vocabulary. They let you signal intent: this logic is local, ephemeral, and contextual. Used judiciously, they reduce noise, improve readability, and keep namespaces clean. The next time you're about to declare a class that will only ever be instantiated once, or write a catch `$e`where `$e` is never read, consider reaching for the anonymous version. PHP put it there for exactly that moment. --- # PHP Native Attributes quick reference Source: https://www.exakat.io/php-native-attributes-quick-reference/ # PHP native attributes In PHP 8.0, PHP added [native attributes](https://www.php.net/manual/en/language.attributes.overview.php) to its vast arsenal of features. Later, the first native attribute, aka, available in the core of PHP, appeared. Here they are, for quick reference. In PHP 8.5, there are 8 native attributes. - PHP 8.0 Attribute - PHP 8.1 ReturnTypeWillChange - SensitiveParameter - PHP 8.2 AllowDynamicProperties - PHP 8.3 Override - PHP 8.4 Deprecated - PHP 8.5 NoDiscard - DelayedTargetValidation - PHP 8.6 No new attribute yet (Apr. 2026) ## PHP Native Attributes [`Attribute`](https://www.php.net/manual/en/class.attribute.php) is the attribute to make a class a valid `Attribute` class. This definition sound pretty meta, but it is quite simple : there must be a first class that defines what an attribute is. It is not compulsory, although, as a gesture of courtesy to the next developer : just add it to every attribute class, and all will be fine. Available since PHP 8.0. ## ReturnTypeWillChange [`ReturnTypeWillChange`](https://www.php.net/manual/en/class.returntypewillchange.php) characterize a method, whose returntype will change in child classes. This prevents PHP from emitting an error about a possible type mismatch. It was created when the native PHP method `JsonSerializable::jsonSerialize()`received the `mixed` returntype. Although that type is the universal type, as it accepts everything, PHP reports a type mismatch with any method which doesn't use that type. Adding this attribute relax that constraint. It only works with PHP native methods, and has no effect on custom methods. Here is a list of PHP native interfaces and methods, which may require their usage : - JsonSerializable::jsonSerialize() - SessionHandlerInterface::open() - SessionHandlerInterface::close() - SessionHandlerInterface::read() - SessionHandlerInterface::write() - SessionHandlerInterface::destroy() - SessionHandlerInterface::gc() - Iterator::current() - Iterator::next() - Iterator::key() - Iterator::valid() - Iterator::rewind() - Countable::count() - IteratorAggregate::getIterator() - php_user_filter::filter() - ArrayAccess::offsetGet() - ArrayAccess::offsetSet() - ArrayAccess::offsetUnset() - ArrayAccess::offsetExists() - RecursiveIterator::getChildren() - FilterIterator::accept() - Exception::__wakeup() Available since PHP 8.1. ## SensitiveParameter [`SensitiveParameter`](https://www.php.net/manual/en/class.sensitiveparameter.php) mark arguments as security sensitive, so that they will be automatically masked when a debug backtrace is displayed. Here is an example : With PHP 8.2, the result is the following : Array ( [0] => Array ( [file] => /Users/famille/Desktop/analyzeG3/test.php [line] => 7 [function] => foo [args] => Array ( [0] => my [1] => SensitiveParameterValue Object ) ) ) Properties are not covered by this parameter. For them, there is already the [__debugInfo()](https://www.php.net/manual/en/language.oop5.magic.php#object.debuginfo), which may be used to hide any sensitive value, instead of displaying it. Available in PHP 8.2. ## AllowDynamicProperties [`AllowDynamicProperties`](https://www.php.net/manual/en/class.allowdynamicproperties.php) relaxes the compulsory property definition that starts with PHP 8.2. The default behavior is now to declare explicitely all properties. Yet, since dynamic properties are a valid use case, albeit a rare one, it is possible to relax this constraint by using this attribute. Another option is to use the `Stdclass` or extend it. This class is built-in with that attribute. Available in PHP 8.2. ## Override The [`Override`](https://www.php.net/manual/en/class.override.php) attribute is used to explicitly indicate that a method in a subclass is overriding a method in the parent class. This attribute has no special effect, unlike other PHP native attribute. It simply state the intention to override a parent method. It is mainly used for documentation and be used by static code analysers. Available in PHP 8.3. ## Deprecated The [`Deprecated`](https://www.php.net/manual/en/class.deprecated.php) attribute is used to explicitly indicate that a method, a function or a class constrant is obsolete, and will be removed in the future. This means the current version is supporting the associated feature, so it is good to use it. It is also recommended to read the documentation, and replace this structure with a modern version, so as to be ready when the change is applied. [`Deprecated`](https://www.php.net/manual/en/class.deprecated.php) does not work for classes, interfaces, enumerations nor traits. Available in PHP 8.4. ## NoDiscard The [NoDiscard](https://www.php.net/manual/en/class.nodiscard.php) attribute is used to explicitly indicate that a method or a function is returning a value that should be processed, and not be ignored. This might be the case of a list of error, or an empty value that means error. `NoDiscard` works on methods and functions. Available in PHP 8.5. ## DelayedTargetValidation The DelayedTargetValidation attribute is meant to make other PHP engine attributes optional. It will shut down complains from the PHP engine for attributes, allowing for future compatibility. Check the RFC : [https://wiki.php.net/rfc/delayedtargetvalidation_attribute](https://wiki.php.net/rfc/delayedtargetvalidation_attribute) Available in PHP 8.5 --- # Renaming Parameters in a method Source: https://www.exakat.io/renaming-parameters-in-a-method/ # Renaming Parameters in a method Renaming parameters in a method used to be an innocuous operation: one could do it without impact. Since PHP 8.0, such renaming may break existing calls. With named parameters, not only the method name, but also its parameters are part of the call. And it is a common challenge to update parameter names, especially in large or old codebases. The problem arose from changing parameter positions, but nowadays, it is also linked to parameter names. And with a method being called many times, it is increasingly difficult to refactor it without impact all across the code base, and a perceived risk. Ideally, the goal of renaming a parameter is to ensure backward compatibility while encouraging migration to the new parameter name. Reasons to rename a parameter are beyond the scope of this article. Here are several strategies to refactor a method's parameter name, without breaking code compatibility: # Parameter Aliasing, And Default Values The strategy here is to add the new parameter with a default value, and use logic to check if the new parameter is set. If so, use its value; otherwise, use the old parameter. This approach allows both parameters to coexist, and gives users a sunset period. ### sunset period, precedence One may also decide the precedence: has the old parameter priority, or the new? In fact, over the sunset period, it may actually change, until the old parameter is ultimately removed. ### choosing the default value The default value choice is a bit of a challenge. Here, it is set to `null`, which is outside the domain of the actual parameter. The detection is then obvious and non-conflictual. The drawback is to expand the type of the new parameter, which may become difficult to reduce later. Another option would be to use a special value in the `int` domain, such as `-1`, or else, but it means such value cannot be used for anything else. ### Cluttering the signature On the other hand, this approach clutters the signature with twice the same parameter, and may make it harder to learn usage. In particular, anyone learning by empirically testing code may loose a lot of time figuring out the situation correctly. Most probably, the result of such experiment will hinder modernisation efforts. Consider also as any AI training will keep emphasizing the old parameter for a long time, as per existing literature. ### Consider a warning This may be completed with a warning, that helps alert users about the evolution. ## Adapter Pattern A step above renaming the parameter is renaming the whole method. For this, create an adapter or wrapper method that accepts the old parameter and calls the new method with the new parameter. ``` ``` ### sunset period This way, the old version of the method is still available. It is recommended to have the old method call the new one, so that the logic is not duplicated in the code. It also creates a natural penalty to using the old method, even if very small. ### more clarity The new parameter appears in a distinct method, and not as a refactor. The relay function introduce a duplicate method for the same feature, which may be confusing for users. It may also be time to rename that method anyway. It is also possible to add a warning, like in the previous solution. ## Middleware or Proxy Pattern After renaming the method, comes renaming the whole class. For that, use a middleware or proxy to intercept calls and map old parameter names to new ones. This moves the refactor from the current class to another one. The correct object may be injected where needed, until the refactor happens. The proxy pattern decouples the old and new logic in a cleaner way, with an explicit intent for refactoring the code. It is also easy to remove the proxy later, when everyone has moved to the new signature. It also adds some complexity to the code. Phasing out the proxy class is important, and will reduce complexity later. ## Documentation and Communication Finally, the last solution is the breaking change. Go forward and rename the parameter with the new name. No sunseting, just a breaking change. This means doing such a change in a major version of the application. ### easing the transition Update the documentation, mention the evolution in roadmap presentations, changelogs, and release notes to inform users about the change. They won't be able to migrate until they have their refactor in place, but they might also be aware of the situation, and possible consequences. This gives users time to plan. ### sunset period based on version Here, the sunset period is managed by versioning. Users have to stay on the previous version until they are meeting the new compatibility requirements, and then, they can change. This is valid for a platform with release version, like a released framework or open source tools. It is harder to do when the versions are less obvious, as in a company platform upgrade. ### Automated updates To speed up the process, it is useful to mention automated solutions to handle the renaming: - Use the IDE `rename parameter` features - Use rector to set up a search and replace situation - Use AI to find and update all calls to the method with the correct new parameter name, when applicable As usual, renaming things works well with static names. It get harder when the code uses dynamic parameter naming, or method. ### Just do it This is the rip the architectural band-aid off approach. It's simple for you, but your users might not appreciate the surprise errors on Monday morning. In particular, when little knowledge is shared before the even, it is a sign that the refactor was not organized. The consequences may be a barrage of complaints from users, or, more silently, a delay in the upgrade to the newer versions, or simple quit from using the platform. Consequences don't always make noise. ### Summary Table | Strategy | Backward Compatible | Encourages Migration | Complexity | | -------- | ------------------- | -------------------- | ---------- | | Parameter Aliasing | ✅ Yes | ❌ No | Low | | Adapter Pattern | ✅ Yes | ✅ Yes | Medium | | Middleware/Proxy | ✅ Yes | ✅ Yes | High | | Documentation | ❌ Yes | ✅ Yes | Low | ## Renaming parameters Renaming parameters is harder than it looks. Since PHP 8.0, the internal variable names are no longer a secret; they are part of the public API. Named parameters do add an extra layer of attachement, although positional parameters had the same problems with...well... positions. The situation grows with usage: the more often a method is called, the riskier it becomes to change anything to it: name, type, position, default value, etc. This might be a good starting point to decide what strategy to use. The other one is how much of a sunset period is needed. It boils down to names, which are a permanent commitment: once a name is set, it is difficult to change it later. This applies to natural language itself, and it is often smoother to promote a new word and leave the previous one adrift, rather than force the replacement. --- # Current PHP RFCs in Voting Phase (March 2026) Source: https://www.exakat.io/current-php-rfcs-in-voting-phase-march-2026/ # Current PHP RFCs in Voting Phase (March 2026) The PHP community has five current PHP RFCs in Voting Phase. The PHP community is actively voting on features that are shaping the future of the language. They range from new functions, code federation, development organisation and license aspects. The current, future and old ones are all available at [http://wiki.php.net/](http://wiki.php.net/). Below is a summary of the five RFCs currently in the voting phase, along with links to their official pages for more details. ## PHP License Update ### Purpose This RFC updates the PHP license to reflect modern legal standards and community expectations, ensuring clarity and compliance. The proposed license is a Modified BSD license, which combines the PHP License and Zend Engine Licence into one Open Source Licence. The RFC lays out a roadmap to evolve the current license schemes to the new one. ### Status Discussion started in July 2025, now in voting phase, until on April 4, 2026. ### Link [PHP License Update](https://wiki.php.net/rfc/php_license_update) ## Exempt input type and value validation from BC Break policy ### Purpose This RFC proposes to exempt input type and value validation from the backward compatibility break policy, allowing more flexibility in handling edge cases without breaking existing code. ### Status Voting started on March 21, 2026, and ends on April 4th, 2026. ### Link [Exempt input type and value validation from BC Break policy](http://wiki.php.net/rfc/policy-exempt-type-value-error-bc-policy) ## enum SortDirection ### Purpose This RFC introduces a new SortDirection enum to standardize sorting direction handling across PHP's sorting functions, improving consistency and type safety. ### Status In voting phase as of March 2026. ### Link [enum SortDirection](https://wiki.php.net/rfc/sort_direction_enum) ## DocComments For Function Parameters ### Purpose This RFC adds support for doc comments on function parameters, improving IDE support, static analysis, and documentation quality. ### Status In voting phase as of February 21, 2026, ends on March 31rst, 2026. ### Link [DocComments For Function Parameters](https://wiki.php.net/rfc/parameter-doccomments) ## array_only_keys() and array_except_keys() ### Purpose This RFC proposes two new utility functions, array_only_keys() and array_except_keys(), to filter array keys based on inclusion or exclusion criteria, simplifying common array operations. ### Status In voting phase as of February 20, 2026, ends on March 26th, 2026 ### Link [array_only_keys() and array_except_keys()](https://wiki.php.net/rfc/array_only_except) ## Keep up to date with the current PHP RFCs in Voting Phase Everyone can keep up with the current discussions around RFCs with the [PHP Wiki](http://wiki.php.net/) website. People with a php.net account are eligible to vote. It is also possible to [create RFC](https://wiki.php.net/rfc/howto) for PHP. --- # Calling All Ancestors Source: https://www.exakat.io/calling-all-ancestors/ # PHP Inheritance Challenge: Calling All Ancestors PHP's object-oriented features are exhaustive and modern, yet sometimes life send you a little challenge: who doesn't like a surprise? Today we'll look at a rare inheritance puzzle. In this post, we are calling all ancestors in a class hierarchy. We start with three classes, extending each other in a hierarchy. Each of them has a distinct eponymous method (eponymous is a fancy word for same name). Each version of the method returns a distinct value, for easy identification. Now, the goal, if you accept it, is to call all three versions of that method, from the deepest child class. Sounds easy, right? Let's dive in and see where the trap lies, and how to escape it. ## The 'Calling All Ancestors' Setup We define three classes: `Grandparents`, `Parents`, which extends `Grandparents`, and `Children`, which extends `Parents`. Each has a non‑static method `foo()` that returns a unique string. You might note that all the class names are plural: this helps go around a limitation in this illustration, where `parent` cannot be used as a class name. `parent` might be used as a method or a constant name, but not a class. It is used as a relative class name by PHP, to access the parent class. And that might be useful later. Note also that if PHP has its own exception with using `parent` as a class, British English doesn't use `Childs`, but `Children`. Apparently, exceptions is a common thing in every language, programming or not. Otherwise, nothing fancy: just classic overriding. Now, our challenge: inside the `Children` class, we want a method that calls **all three** `foo()`implementations and returns an array with the results. Let's call it `callAllFoos()`. ## First Attempts: $this and parent Inside `Children`, we can easily call its own `foo()` using `$this`: To call the direct parent’s version, PHP gives us the `parent` keyword. Pay attention, `parent` is a PHP keyword, while `Parents` is the class in the code: That works perfectly. Now we need the grandparent’s version. What about `Grandparents::foo()`? First, `grandparent` is not a PHP keyword, so this is wrong. It would be fun, though, as any prolongement of that allegory would give us `greatgrandparent` for the 3rd level, and `greatgreatgrandparent` for the 4th level, and the longest keyword ever for the 128th level (just because 128 levels of inheritance would be a reasonable limit, I guess?) ## The Real Challenge: Accessing the Grandparent Properly We need a way to call `Grandparent::foo()` on our current object `Children`. `parent` is a good keyword, but it is not possible to chain it to access another level of PHP. PHP does not interpret the right side of the `::` call as a keyword, but as a constant name. And, indeed, it is possible to call a constant `parent`. ### The Failed Trick: Closure Binding A possible plan is to use a closure and bind it with the grandparent's scope: Unfortunately, this **does not work**. The scope parameter in `bindTo` only affects visibility, which make private/protected methods accessible, and static method resolution. For non‑static calls, the method dispatch is still based on the actual class of the object (`Child`). So `$this->foo()` inside the closure will still call `Children::foo()`, not `Grandparents::foo()`. So we need another approach. ### The Successful Trick: Static Method call Obviously, this is a static call to a non‑static method. Up to PHP 7, this would trigger a deprecation warning; in PHP 8, it is a fatal error. It is also a bad practice. So technically, this line does not work. And yet... Just like calling `parent::foo()` looks like a static call, but is, in fact, dependent on the method's actual signature, and `Grandparents::foo()`works here. It is because `Grandparents` is part of the current class's hierarchy. In fact, not only it is possible to call that non-static method statically, but PHP goes the extra mile, and even assign `$this`, as one would expect in a non-static method. Et voila! ### The Other Successful Trick: Static Method call [Julio J](https://phpc.social/@j3j5@hachyderm.io) found another way, by using the [forward_static_call ](https://www.php.net/forward_static_call)function: it actually works the same way has the syntax above, using the same parameters. Also, $this is available in the method. ### A Reliable Solution: Reflection Another possible solution is to use the always reliable PHP's `ReflectionMethod`: `ReflectionMethod::invoke($object)` calls the method on the given object, using the method's original definition (from `Grandparents`). This bypasses any overriding. Let's test it: Output: ``` Array ( [0] => Children [1] => Parents [2] => Grandparents ) ``` `ReflectionMethod` represents the method as defined in a specific class. When we invoke it on an object, it calls that exact method implementation, regardless of whether the object's class overrides it. The object must be an instance of that class or a subclass, which it is, and the method must be accessible (if it's private, we'd need `setAccessible(true)`). In our case, it's public, so it's fine. ## Conclusion PHP's inheritance model gives you direct access to the current class (`$this`) and the immediate parent (`parent`), but ancestors beyond that are hidden, unless the methods are not overriden by the lower levels. However, with a static call or with reflection, it is possible to reach any level of the hierarchy. This little puzzle shows that even on a beaten path like OOP, there are still hidden corners and a special way for PHP to save the day. So, now, it is possible to emulate a `::parent`operator, but what about a `::grandparent`operator, like the `::class` one? You might be in luck... --- # Refactoring Strings to Enums Source: https://www.exakat.io/refactoring-strings-to-enums/ # Refactoring Strings to Enums This article describes the journey of refactoring a not-so-old piece of code from using strings to using enumerations. ## Original Reasons When Exakat was started in 2014, enumerations were not even on the radar. Strings, and sometimes constants, were the obvious solution. As the original concept evolved quickly, strings were used everywhere, and they naturally remained in place. ### Modernizing PHP Code Converting those strings to constants was considered several times, but never implemented. Writing `VARIABLE` or `Atom::VARIABLE` instead of `'Variable'` felt mostly cosmetic. Readability might have improved slightly, but even that was debatable. More importantly, there was no way to enforce the use of constants. PHP replaces constants with their values, so both `foo('Variable')` and `foo(Atom::VARIABLE)` remain valid. Only static analysis tools could reliably detect misuse. ### Smaller Footprint Enumerations change this dynamic. Instead of crowding classes with constants, enum cases live in their own dedicated structure. More importantly, enums introduce typing: `Atomname::VARIABLE` is an object of type `Atomname`, while `'Variable'` is just a string. Correct values are now enforced at the engine level. There was also an expectation of performance improvements. Enumeration cases are single instances reused across the application, while string literals are repeated. In practice, however, I never managed to produce a meaningful benchmark showing a real speed gain. In fact, in very large loops, strings may even be slightly faster. ### Easier Analysis Writing a static analysis tool also gives a particular perspective. Strings represent about 11% of the source code in Exakat. Comparable figures appear in other projects: WordPress (~13%), Typecho (~14%), Laravel (~35%), Leaf (~8%), Contao (~10%), and Slim (~2%). By comparison, class constants and enum cases exist in far smaller numbers, making them faster to process. Beyond quantity, constants and enum cases are easier and safer to locate and analyze than strings. The strings targeted by this refactor represent only a fraction of all strings in the codebase. Previously, all strings were analyzed together: array indexes, values, interpolated fragments, default arguments, and so on. By isolating atom names into a dedicated enum, analysis becomes both faster and more precise. Before moving to the refactor itself, it is worth emphasizing that static analysis tools should adapt to the code, not the other way around. Still, modernizing code can make tooling more efficient. It is not mandatory, but it is certainly beneficial. # Refactoring to Enums ## The Obvious Parts ### What Was Refactored? Among the many strings in the source code, atom names stood out as ideal candidates. PHP converts source code into tokens, removing whitespace and structural tokens such as `()` or `{}`. Roughly one third of the remaining tokens are used to rebuild the AST. In Exakat, these tokens are called atoms, each representing a unit of information. Examples include Class, Interface, Addition, Power, Shell, File, Void, and Sequence. There are currently 132 atom names. The list is known at startup, and if one is missing, it usually means work has already started toward supporting a newer PHP version. A backed string enum was therefore an obvious choice. To minimize disruption, the existing naming convention, Uppercase First, was preserved. ### Cleaning Previous Definitions There was nothing to clean up: the values were literals, not constants. So, no classes lost 132 constants. That was a pleasant surprise. Handling a previosu migration from literal strings to constants is left to the reader. One may replace such constants with their enumeration, or also, replace the value in the constant by the enumeration case. Fun and joy, working with the code. ### Code Updates Most updates consisted of straightforward replacements. Not exciting, but effective. In total, slightly over five hundred string replacements were performed. A few type declarations were also updated. In practice, only one property and a small number of related methods ended up using the enumeration directly. ## The Less Obvious Parts This is where the unexpected appeared. Some issues were anticipated, but the real surprises were, by definition, the ones that were not. ### Not All Strings Should Be Converted This was expected from the beginning, and in fact one of the goals: separating atom names from all other strings. Replacing patterns such as `/^[A-Z][a-z]+$/` with enum cases would have been far too broad. It would also match unrelated strings such as Damien or Seguy, which are clearly not AST elements. Even legitimate-looking words occasionally belonged to different contexts. Those cases were handled manually, or detected through static analysis and tests. ### Finding Non-Existent Cases One major benefit of enums is that they form a closed set. Any value outside that set becomes an error. This immediately revealed several outdated cases in large switch statements. Some atom names had been renamed long ago, while the old string values remained silently in the code, doing nothing except consuming a few CPU cycles. ``` ?> ``` Such silent dead code is a long-term problem. Over time, it increases complexity in handling, reviews, testing, and even performance discussions. ### Removing default in switch or match? Another side effect is that static analyzers now complain about default branches. With enums, all possible cases are known, making default theoretically unnecessary. In practice, I kept the default branch. The enum may grow in the future, and new cases would otherwise remain unhandled. The default is therefore a safeguard for future evolution, not for current logic. Static analysis will still help identify missing cases when the time comes. ### The Edge Case of Class PHP had one surprise left. Converting 'Class' to Atomname::Class collided with the special ::class keyword used to retrieve fully qualified class names. Because class is reserved and case-insensitive, the enum case could not compile. The solution was to rename it to Atomname::_Class. Naming exceptions are never pleasant, but sometimes unavoidable. Static analysis helped detect incorrect comparisons during this transition, as strings and enum objects cannot be compared directly. ### Dynamic String Processing vs Enumerations Some atom names were previously manipulated as strings. For example, building new atom names through concatenation: This approach does not translate well to enums. Fortunately, such cases were rare and were replaced with match() expressions. ### Exporting Enumeration Cases Exporting enums means converting them back to strings when data leaves the application. In this case, that mainly meant storing data in the graph database used by Exakat. Because the enum is backed, each case already provides its string representation through the value property: Enums do not support __toString() or direct casting, so this explicit access is required. ### Importing Enumeration Cases Importing deserves equal attention. Reading strings from storage normally requires converting them back into enum cases. In this specific case, it does not matter much. Once data is stored in the database, it is mostly processed there. Atom names remain an internal concept and are not re-imported into PHP frequently. The situation would be different for user data, where validation becomes essential. This is an important consideration when deciding whether to move from strings to enums. ## Final Thoughts The conversion from strings to enumerations turned out to be far more eventful than expected, which probably explains why it had been postponed for so long. One non-obvious difficulty was the mismatch between treating strings as indivisible tokens conceptually and actually enforcing that constraint through enums. Some parts of the code relied on dynamic string manipulation, which required rethinking rather than simple replacement. The refactored code ended up about 2% larger. Lines became longer, and some calls had to be split to preserve readability. However, the refactor also revealed unused code and hidden bugs, a common side effect of large structural changes. Ultimately, moving code forces a closer review of assumptions, and that alone tends to improve code quality. --- # All PHP 8.5 hash algorithms Source: https://www.exakat.io/all-php-8-5-hash-algorithms/ # All PHP 8.5 hash algorithms Choosing the right hashing algorithm is difficult, balancing the need for security, speed, and reliability. PHP offers a almost 60 built-in hashing options, but not all are created equal : some are industry standards for cryptographic security, while others are optimized for lightning-fast non-cryptographic tasks like caching or checksums; some are just here for legacy reasons. This blog post provides a comprehensive, categorized guide to every algorithm available in PHP, helping you navigate the trade-offs between legacy compatibility and modern security to ensure you're always using the right tool for the job.         ## Hashing families - MD - SHA-1 - SHA-2 - SHA-3 - RIPEMD - Whirlpool - Tiger - Snefru - GOST - Checksum - FNV - Jenkins - MurmurHash - xxHash - Haval ## MD ### md2 [More details](https://en.wikipedia.org/wiki/MD5) md2 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: fast, widely used. Usage: non-cryptographic caching, legacy identifier generation. ### md4 [More details](https://en.wikipedia.org/wiki/MD5) md4 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: fast, widely used. Usage: non-cryptographic caching, legacy identifier generation. ### md5 [More details](https://en.wikipedia.org/wiki/MD5) md5 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: fast, widely used. Usage: non-cryptographic caching, legacy identifier generation. ## SHA-1 ### sha1 [More details](https://en.wikipedia.org/wiki/SHA-1) sha1 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: legacy compatibility. Usage: non-cryptographic caching, legacy identifier generation. ## SHA-2 ### sha224 [More details](https://en.wikipedia.org/wiki/SHA-2) Strengths: high security, industry standard. Usage: cryptographic security, digital signatures. ### sha256 [More details](https://en.wikipedia.org/wiki/SHA-2) Strengths: high security, industry standard. Usage: cryptographic security, digital signatures. ### sha384 [More details](https://en.wikipedia.org/wiki/SHA-2) Strengths: high security, industry standard. Usage: cryptographic security, digital signatures. ### sha512/224 [More details](https://en.wikipedia.org/wiki/SHA-2) Strengths: high security, industry standard. Usage: cryptographic security, digital signatures. ### sha512/256 [More details](https://en.wikipedia.org/wiki/SHA-2) Strengths: high security, industry standard. Usage: cryptographic security, digital signatures. ### sha512 [More details](https://en.wikipedia.org/wiki/SHA-2) Strengths: high security, industry standard. Usage: cryptographic security, digital signatures. ## SHA-3 ### sha3-224 [More details](https://en.wikipedia.org/wiki/SHA-3) Strengths: modern, resistant to length extension attacks. Usage: cryptographic security, future-proof hashing. ### sha3-256 [More details](https://en.wikipedia.org/wiki/SHA-3) Strengths: modern, resistant to length extension attacks. Usage: cryptographic security, future-proof hashing. ### sha3-384 [More details](https://en.wikipedia.org/wiki/SHA-3) Strengths: modern, resistant to length extension attacks. Usage: cryptographic security, future-proof hashing. ### sha3-512 [More details](https://en.wikipedia.org/wiki/SHA-3) Strengths: modern, resistant to length extension attacks. Usage: cryptographic security, future-proof hashing. ## RIPEMD ### ripemd128 [More details](https://en.wikipedia.org/wiki/RIPEMD) ripemd128 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: alternative to sha. Usage: cryptographic security (160+ bits), blockchain (ripemd-160). ### ripemd160 [More details](https://en.wikipedia.org/wiki/RIPEMD) Strengths: alternative to sha. Usage: cryptographic security (160+ bits), blockchain (ripemd-160). ### ripemd256 [More details](https://en.wikipedia.org/wiki/RIPEMD) ripemd256 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: alternative to sha. Usage: cryptographic security (160+ bits), blockchain (ripemd-160). ### ripemd320 [More details](https://en.wikipedia.org/wiki/RIPEMD) ripemd320 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: alternative to sha. Usage: cryptographic security (160+ bits), blockchain (ripemd-160). ## Whirlpool ### whirlpool [More details](https://en.wikipedia.org/wiki/Whirlpool_(hash_function)) Strengths: large 512-bit hash, based on aes. Usage: high-security cryptographic hashing. ## Tiger ### tiger128,3 [More details](https://en.wikipedia.org/wiki/Tiger_(hash_function)) Strengths: designed for 64-bit systems. Usage: fast hashing on 64-bit systems. ### tiger160,3 [More details](https://en.wikipedia.org/wiki/Tiger_(hash_function)) Strengths: designed for 64-bit systems. Usage: fast hashing on 64-bit systems. ### tiger192,3 [More details](https://en.wikipedia.org/wiki/Tiger_(hash_function)) Strengths: designed for 64-bit systems. Usage: fast hashing on 64-bit systems. ### tiger128,4 [More details](https://en.wikipedia.org/wiki/Tiger_(hash_function)) Strengths: designed for 64-bit systems. Usage: fast hashing on 64-bit systems. ### tiger160,4 [More details](https://en.wikipedia.org/wiki/Tiger_(hash_function)) Strengths: designed for 64-bit systems. Usage: fast hashing on 64-bit systems. ### tiger192,4 [More details](https://en.wikipedia.org/wiki/Tiger_(hash_function)) Strengths: designed for 64-bit systems. Usage: fast hashing on 64-bit systems. ## Snefru ### snefru [More details](https://en.wikipedia.org/wiki/Snefru) snefru is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: historical interest. Usage: historical interest only. ### snefru256 [More details](https://en.wikipedia.org/wiki/Snefru) snefru256 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: historical interest. Usage: historical interest only. ## GOST ### gost [More details](https://en.wikipedia.org/wiki/GOST_(hash_function)) gost is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: russian national standard. Usage: cryptographic security (standard-specific). ### gost-crypto [More details](https://en.wikipedia.org/wiki/GOST_(hash_function)) Strengths: russian national standard. Usage: cryptographic security (standard-specific). ## Checksum ### adler32 [More details](https://en.wikipedia.org/wiki/Adler-32) adler32 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: faster than crc32 but less reliable. Usage: checksum, error detection. ### crc32 [More details](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) crc32 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: error detection in data transmission. Usage: checksum, error detection. ### crc32b [More details](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) crc32b is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: error detection in data transmission. Usage: checksum, error detection. ### crc32c [More details](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) crc32c is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: error detection in data transmission. Usage: checksum, error detection. ## FNV ### fnv132 [More details](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) fnv132 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: low collision rate, simple. Usage: fast hash table keys, non-cryptographic identifier generation. ### fnv1a32 [More details](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) fnv1a32 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: low collision rate, simple. Usage: fast hash table keys, non-cryptographic identifier generation. ### fnv164 [More details](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) fnv164 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: low collision rate, simple. Usage: fast hash table keys, non-cryptographic identifier generation. ### fnv1a64 [More details](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) fnv1a64 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: low collision rate, simple. Usage: fast hash table keys, non-cryptographic identifier generation. ## Jenkins ### joaat [More details](https://en.wikipedia.org/wiki/Jenkins_hash_function) joaat is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: fast non-cryptographic hash. Usage: fast non-cryptographic identifier generation. ## MurmurHash ### murmur3a [More details](https://en.wikipedia.org/wiki/MurmurHash) murmur3a is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: fast non-cryptographic hash for hash tables. Usage: high-performance hash table indexing, caching. ### murmur3c [More details](https://en.wikipedia.org/wiki/MurmurHash) murmur3c is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: fast non-cryptographic hash for hash tables. Usage: high-performance hash table indexing, caching. ### murmur3f [More details](https://en.wikipedia.org/wiki/MurmurHash) murmur3f is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: fast non-cryptographic hash for hash tables. Usage: high-performance hash table indexing, caching. ## xxHash ### xxh32 [More details](https://github.com/Cyan4973/xxHash) xxh32 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: extremely fast non-cryptographic hash. Usage: extremely fast non-cryptographic data processing, caching. ### xxh64 [More details](https://github.com/Cyan4973/xxHash) xxh64 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: extremely fast non-cryptographic hash. Usage: extremely fast non-cryptographic data processing, caching. ### xxh3 [More details](https://github.com/Cyan4973/xxHash) xxh3 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: extremely fast non-cryptographic hash. Usage: extremely fast non-cryptographic data processing, caching. ### xxh128 [More details](https://github.com/Cyan4973/xxHash) xxh128 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: extremely fast non-cryptographic hash. Usage: extremely fast non-cryptographic data processing, caching. ## Haval ### haval128,3 [More details](https://en.wikipedia.org/wiki/HAVAL) haval128,3 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval160,3 [More details](https://en.wikipedia.org/wiki/HAVAL) haval160,3 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval192,3 [More details](https://en.wikipedia.org/wiki/HAVAL) haval192,3 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval224,3 [More details](https://en.wikipedia.org/wiki/HAVAL) haval224,3 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval256,3 [More details](https://en.wikipedia.org/wiki/HAVAL) haval256,3 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval128,4 [More details](https://en.wikipedia.org/wiki/HAVAL) haval128,4 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval160,4 [More details](https://en.wikipedia.org/wiki/HAVAL) haval160,4 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval192,4 [More details](https://en.wikipedia.org/wiki/HAVAL) haval192,4 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval224,4 [More details](https://en.wikipedia.org/wiki/HAVAL) haval224,4 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval256,4 [More details](https://en.wikipedia.org/wiki/HAVAL) haval256,4 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval128,5 [More details](https://en.wikipedia.org/wiki/HAVAL) haval128,5 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval160,5 [More details](https://en.wikipedia.org/wiki/HAVAL) haval160,5 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval192,5 [More details](https://en.wikipedia.org/wiki/HAVAL) haval192,5 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval224,5 [More details](https://en.wikipedia.org/wiki/HAVAL) haval224,5 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). ### haval256,5 [More details](https://en.wikipedia.org/wiki/HAVAL) haval256,5 is not a secure hashing anymore. Do not use it for security sensitive operations. Strengths: variable length and rounds. Usage: flexible cryptographic hashing (variable output). --- # Meetup Limoges 29 janvier 2026 Source: https://www.exakat.io/meetup-limoges-29-janvier-2026/ # Meetup Limoges 29 janvier 2026   Nous serons de de passage à l' [@afup](https://phpc.social/@afup@mastodon.online) Limoges, jeudi 29 janvier 2026. Rendez-vous chez [OD&B,](https://www.odandb.com/) pour améliorer son code PHP aujourd'hui et être prêt pour [#PHP9](https://phpc.social/tags/PHP9) demain! Les elePHPants sont les bienvenus! [Réservez votre place au meetup Limoges 29 janvier 2026](https://www.meetup.com/afup-limoges-php/events/312898152/)       ## Préparez votre code pour PHP 9: le panorama des features obsolètes en PHP 8 Et si se préparer à PHP 9 dès aujourd’hui permettait d’écrire un meilleur code dès aujourd'hui ? Je vous propose une présentation autour des features obsolètes de PHP 9 (oui, PHP 9 😏) pour montrer comment : - prendre en compte les dépréciations actuelles - améliorer la lisibilité et la robustesse du code dès aujourd’hui, - rendre la migration beaucoup plus simples le moment venu. Penser PHP 9, ce n’est pas spéculer sur l’avenir : c’est écrire du PHP plus propre, plus durable, dès maintenant. --- # Convert a string to integer with PHP Source: https://www.exakat.io/convert-a-string-to-integer/ # Convert a string to integer with PHP PHP 8.5 provides a new error message when one tries to convert a string to integer with PHP. [`The float-string “%s” is not representable as an int, cast occurred`](https://php-errors.readthedocs.io/en/latest/messages/the-float-string--%22%25s-%22-is-not-representable-as-an-int%2C-cast-occurred.html) This is new with this version, and besides the warning itself, it is not even a [changed behavior](https://php-changed-behaviors.readthedocs.io/en/latest/). It is the classic PHP behavior when casting a string into an integer. Let's see what is happening here. ## Convert a string to integer The most common way to convert a string to integer is to use the `(int)` cast operator. It takes anything, and then, returns an integer. This is quite safe, and also an impressive feat as the incoming type is really `mixed`. ## Convert a string to integer, with a warning Obviously, there are several situations where the conversion to integer fails. For example, when the string does not contains numbers, it is converted to `0`. Yet, there are no warning produced when the string is unparsable. PHP fails silently, and returns 0. It is actually a good practice to check for `0` after the cast: depending on the situation, it might actually be an error, just like `strpos()` returning false. So, where does the warning come from? It happens when PHP manages to convert the string, but the resulting integer is larger that the largest PHP integer. ## Making sense of the conversion result It is easy to recognize `9223372036854775807`as the value of `PHP_INT_MAX`. If you thought it was `4294967295`, then, you are still on a PHP 32-bits architecture. When the original string yields a larger than `PHP_INT_MAX` value, PHP caps its value to the maximum, and emits a warning. This helps distinguish between an error where the string is not an integer, with a case where it is a possible integer, just a bit too big. ## And it gets weirder And then, it gets weirder. With 30 `1`, the string yields a `PHP_INT_MAX`and with 500 `1`, it yields a `0`. Now... there could be a limit around `170141183460469231731687303715884105727`, which would be `PHP_INT_MAX` for a 128-bits architecture (Thanks to [bcmath](https://www.php.net/manual/en/function.bcpow.php) for the quick calculation). But a quick check also get `0` with 310 `1` in a string. The limit is actually around another, lesser known constant, which is also a close cousin of `PHP_INT_MAX` : `PHP_FLOAT_MAX`. It is `1.7976931348623E+308`. ## And then, some edge cases Now, no one will send a 310 character long number via HTTP to PHP, for conversion. This looks suspicious enough to be the work of some intrusion attempt, or a script kiddy, or simply, a bug. Yet, while looking at the maximum float, one may recognize something we have skipped so far: an integer string is not build only with digits. We can see `.`, `E` and `+` in the number. All those look like special characters.... let's check them. ### Space Let's start with space, ``, which is not listed above but comes immediately to mind. Spaces are automatically trimmed from the beginning and the end of the string. Spaces, and also new line, carriage return, both tabulations, and line feed. ## Non space, non digit chars When the space is in the middle of the string, PHP stops the conversion, and returns the integer so far. It is actually the same behavior when the string is partially made of digits. PHP parse what it can, and then, stops at the first weird character. ### And here comes the E One letter that is not weird in a number is `E`. It is used to build the decimal format, as we saw with `PHP_FLOAT_MAX`. And it gets a special behavior: `E` is part of the decimal format, and represents the separator between the mantis and the exponent. So, PHP reads it as `123 * 10 ^ 4` and ends up with `1230000`. So, the string is much larger. At that point, it is interesting to note that there is no more need for a long string to break the size of integers. ### The case of the dot Now that `E` has been introduced, the last weird character is dot `.`. It is also a part of the decimal format, and, when used alone in the string, its trailing characters are abandonned: after all, they are part of the decimal part of the number. When the `.` has a following `E`, then the exponent is applied. In the example above, it moves the comma two positions to the right, and only the final `6` is abandonned. Note that this is not a rounding, but a truncature: the `6` is not even processed. ### Plus and Minus Finally, plus `+` and minus `-` are accepted in the string. They must be at the beginning of the string, and alone (unlike PHP which parses multiple occurrences of them). A second sign may be used with the exponent too. ## The use of the (int) warning The warning that PHP 8.5 emits with the cast to integer does not change anything to the way PHP parses numbers. This process has been for decades, and it is not ready to change. Now, the emission of the warning leads to more work. Hopefully, it is not displayed directly to the user on the web application. A quick search on your favorite search engine yields quite a few online apps that are displaying it (not naming any culprits, find your own). A better approach is to use the [display_errors](https://www.php.net/manual/en/errorfunc.configuration.php#ini.display-errors)directive, and set it to `0` (or a very large cast string...) and rather, use [log_errors](https://www.php.net/manual/en/errorfunc.configuration.php#ini.log-errors). Yet, this means that the devops have a new error message polluting their logs, and it needs to be addressed in the code. The question becomes : ## How to check when a string can be converted to integer? Indeed, it would be nice to have an equivalent of `json_validate()`: it validates the string, but does not parse the string. That would skip the warning, and allow the code to handle the weird situation immediatly. But how to do that? Here are several solutions. ## Use intval() [`intval`](https://www.php.net/manual/en/function.intval.php) yields the same warning when the string is too big for an integer. ## Use settype() [`settype`](https://www.php.net/manual/en/function.settype.php) yields the same warning when the string is too big for an integer. Also, nobody uses that function. ## Use is_int() [`is_int`](https://www.php.net/manual/en/function.is-int.php) checks the type of the variable. It is already known that is a `string`, so this fails. Yet, everyone tries it, at least once. ## Use is_numeric() [`is_numeric`](https://www.php.net/manual/en/function.is-numeric.php) checks if the string may be converted to a number. That means both integers and decimals. So, if the string is a valid decimal, but not a valid integer, the warning still happens at cast time. Also, is_numeric() does not take into account the limits integers not decimals, so it reports that 3000 ones is a valid number. And it is not. ## Big number functions: BCMath and GMP PHP has, at least, two extensions to handle really large numbers: [`bccomp`](https://www.php.net/manual/en/function.bccomp.php) and [`gmp_cmp`](https://www.php.net/manual/en/function.gmp-cmp.php). They both take in strings, or integers, so this works as an early warning system. In both cases, they do not support numeric-strings like PHP, in particular `E` or any non-digit character. These require another try/catch structure to be safe. ## The filter extension Closer to our use case is the filter extension. This extensions has a few functions to validate incoming data: in this case, [`filter_var`](https://www.php.net/manual/en/function.filter-var.php). There is a dedicated option `FILTER_VALIDATE_INT` for that purpose, with a side option: The `max_range` option works with its default values, but also, with other values, as long as it is in the valid range of PHP integers (indeed, it is possible to specify `max_range` as ... a string). ## The revenge of the noscream Finally, there is a valid option: `@`. It doesn't change anything to the string interpretation, provides backward compatibility, and ... hides warnings! `@` is usually slow, but it might be faster than calling a separate function to avoid it. It is also easier to complete the current syntax. To be honest, I never though about using `@` on a cast operator (it works!), nor about recommending using it in 2026. But, there we are. # Converting a string to integer is not obvious As usual, there is a lot of knowledge to gain by looking at how the code works. The most interesting part here is how it might be easy to trigger warnings and strange behaviors on application, by feeding them with too large integers. Such integers might be validated, and used in the database as primary key. The damage might not be important. But it will create strange errors in the logs, which might be difficult to interpret and fix. Hopefully, this post might help understanding the situation, and taking the right steps to mitigate it. Happy Code Auditing! --- # PHP 8.5 new error messages Source: https://www.exakat.io/php-8-5-new-error-messages/ # PHP 8.5 new error messages With a host of new features, PHP 8.5 also brings [another 265 new error messages](https://php-errors.readthedocs.io/en/latest/). These new error messages appears as PHP pays more attention to received arguments: their type, their values are reviewed more carefully before being used. And some error messages are prone to appear more often than others. Here is a small selection. ## syntax error, unexpected token ">" Technically, this error is not a new PHP 8.5 error, but rather a more popular PHP 8.4 and older error. When downgrading from PHP 8.5 to an older version, while there are some pipe operator in the code, PHP raises this syntax error: It simply signals that the code should only be run on PHP 8.5, or it needs to be refactored to run on older PHP versions. ## Arrow functions on the right hand side of |> must be parenthesized PHP 8.5 is all about the new pipe operator. It allows the chaining of methods from left to right, instead of the nested approach that was available so far. There are a few constraints on this new operator : it must chain closures with one parameter only. Every kind of PHP closures and callable may be used, which is, at least, a lot: This is all what would be expected in PHP code, except the last one. It is not possible to use directly an arrow function. They must be put between parenthesis to be parsed and understood by PHP. This is a compilation error, with an explicit message, and the needed usage of the new Pipe operator. This error will happen was the code is being refactored to take advantage of the new features. This will be part of the initial learning curve, and it shall not be a surprise. ## Using null as an array offset is deprecated, use an empty string instead One change in behavior in PHP 8.5 is data: it is the deprecation of using `null` as an array index. Until now, `null` is silently converted to empty string, and use as is. At time, the whole process may be completly invisible to the calling code. This deprecation warning is bound to fill quite a lot of logs, as using `null` is common, and it is completely invisible. The simplest option is to convert explicitly the `null`value to empty string with the coalesce `??` operator. It makes the deprecation go away, and preserve the previous behavior. It might also be time to check if `null` and `''`empty string are, indeed, the same case, or if some error management is needed. Both situations are plausible, but require some attention. ## Non-canonical cast (integer) is deprecated, use the (int) cast instead For each cast operation, there is a twin version. For example, `(int)` and `(integer)` are both available to convert a piece of data into to an integer. This is also the case for the obvious `(bool)` and `(boolean)`, for the less obvious `(float)` and `(double)`, and the suprising `(string)` and `(binary)`. While they exist, they are very rarely used in any code, and are quite easy to fix. They may also be related to the `int` scalar type, which does not support the `integer` type: the latter may be even defined as a class, and used as such. ## The predefined locally scoped $http_response_header variable is deprecated, `http_response_header` is a PHP reserved variable, which appears when PHP submit a request over the network for a file, such as with `file_get_contents()` or `fopen()`. Then, that variable holds the received HTTP headers, just as the name suggest. The code above results in this output: ``` PHP Warning: Undefined variable $http_response_header Warning: Undefined variable $http_response_header NULL array(13) { [0]=> string(15) "HTTP/1.1 200 OK" [1]=> string(17) "Server: myracloud" [2]=> string(35) "Date: Tue, 11 Nov 2025 09:27:13 GMT" ... more headers after that ``` Since PHP 8.4, the native function `[http_get_last_response_headers](https://www.php.net/manual/en/function.http-get-last-response-headers.php)()` is available to access these headers. It should be used to replace any access to the `$http_response_headerline` and be ready for PHP 9. ## syntax error, unexpected token "const" Another downgrading syntax error happens with attributes on global constant. The attribute is parsed by PHP 8.*, yet it is not valid with a global constant with PHP 8.4 and older. This yields this syntax error. It may be hard to understand as class constants accept attributes, and global constants look very much alike. ## Happy PHP 8.5 migration With more explicit error messages, PHP 8.5 brings a better view over code compilation and execution. Every PHP application benefit from compiling with this new version, even on older versions: it provides valuable insights on how to improve one's code and make it future proof. --- # Understanding All Relations Between Classes, Interfaces, Traits, and Enums in PHP Source: https://www.exakat.io/understanding-all-relations-between-classes-interfaces-traits-and-enums-in-php/ # Understanding Relationships Between Classes, Interfaces, Traits, and Enums in PHP Modern PHP offers four major code structures — **classes**, **interfaces**, **traits**, and **enums** — each with its own purpose and rules. But the real power of PHP comes from how these structures can **interact with each other**. This page gathers all relations between classes in PHP. If you’ve ever wondered *“Can an enum use a trait?”* or *“Can a trait implement an interface?”*, the answer lies in PHP’s keywords: `extends`, `implements`, and `use`. These keywords define the legal relationships between the building blocks of your code. To make this crystal clear, here’s a matrix showing **who can interact with whom** — and with which keyword. | Can interact with… | **  class  ** | ** interface** | **    trait    ** | **  enum  ** | | -------------------- | ---------------- | --------------- | ---------------------- | --------------- | | [**class**](https://php-dictionary.readthedocs.io/en/latest/dictionary/class.ini.html) | ✅ | ✅ | ✅ | ❌ | | [**interface**](https://php-dictionary.readthedocs.io/en/latest/dictionary/interface.ini.html) | ❌ | ✅ | ❌ | ❌ | | [**trait**](https://php-dictionary.readthedocs.io/en/latest/dictionary/trait.ini.html) | ❌ | ❌ | [✅](https://php-dictionary.readthedocs.io/en/latest/dictionary/trait.ini.html) | ❌ | | [**enum**](https://php-dictionary.readthedocs.io/en/latest/dictionary/enum.ini.html) | ❌ | ✅ | ✅ | ❌ | ## Longer version (with links) | Can interact with… | **class** | **interface** | **trait** | **enum** | | -------------------- | --------- | ------------- | --------- | -------- | | [**class**](https://php-dictionary.readthedocs.io/en/latest/dictionary/class.ini.html) | ✅ [**extends**](https://php-dictionary.readthedocs.io/en/latest/dictionary/implements.ini.html) one [**class**](https://php-dictionary.readthedocs.io/en/latest/dictionary/class)(single inheritance); Or [**composition**](https://php-dictionary.readthedocs.io/en/latest/dictionary/composition.ini.html) | ✅ [**extends**](https://php-dictionary.readthedocs.io/en/latest/dictionary/extends.ini.html) one or multiple [**interface**](https://php-dictionary.readthedocs.io/en/latest/dictionary/interface.ini.html) | ✅ [**use**](https://php-dictionary.readthedocs.io/en/latest/dictionary/use.ini.html) one or multiple [**traits**](https://php-dictionary.readthedocs.io/en/latest/dictionary/trait.ini.html) | ❌ Cannot [**extend**](https://php-dictionary.readthedocs.io/en/latest/dictionary/extends.ini.html) an [**enum**](https://php-dictionary.readthedocs.io/en/latest/dictionary/enum.ini.html) (no inheritance), can only call its cases | | [**interface**](https://php-dictionary.readthedocs.io/en/latest/dictionary/interface.ini.html) | ❌ Cannot [**extend**](https://php-dictionary.readthedocs.io/en/latest/dictionary/extends.ini.html) a [**class**](https://php-dictionary.readthedocs.io/en/latest/dictionary/class.ini.html) | ✅ [**extends**](https://php-dictionary.readthedocs.io/en/latest/dictionary/extends.ini.html) one or multiple [**interfaces**](https://php-dictionary.readthedocs.io/en/latest/dictionary/interface.ini.html) | ❌ Cannot [**use**](https://php-dictionary.readthedocs.io/en/latest/dictionary/use.ini.html) a [**trait**](https://php-dictionary.readthedocs.io/en/latest/dictionary/trait.ini.html) | ❌ Cannot [**implement**](https://php-dictionary.readthedocs.io/en/latest/dictionary/implements.ini.html) or [**extend**](https://php-dictionary.readthedocs.io/en/latest/dictionary/extends.ini.html) an [**enum**](https://php-dictionary.readthedocs.io/en/latest/dictionary/enum.ini.html) | | [**trait**](https://php-dictionary.readthedocs.io/en/latest/dictionary/trait.ini.html) | ❌ Cannot [**extend**](https://php-dictionary.readthedocs.io/en/latest/dictionary/extends.ini.html) a [**class**](https://php-dictionary.readthedocs.io/en/latest/dictionary/class.ini.html) | ❌ Cannot [**implement**](https://php-dictionary.readthedocs.io/en/latest/dictionary/implements.ini.html) an [**interface**](https://php-dictionary.readthedocs.io/en/latest/dictionary/interface.ini.html) | ✅ [**use**](https://php-dictionary.readthedocs.io/en/latest/dictionary/use.ini.html)other [**traits**](https://php-dictionary.readthedocs.io/en/latest/dictionary/trait.ini.html)inside a [trait](https://php-dictionary.readthedocs.io/en/latest/dictionary/trait.ini.html) | ❌ Cannot [**use**](https://php-dictionary.readthedocs.io/en/latest/dictionary/use.ini.html) an [**enum**](https://php-dictionary.readthedocs.io/en/latest/dictionary/enum.ini.html) | | [**enum**](https://php-dictionary.readthedocs.io/en/latest/dictionary/enum.ini.html) | ❌ Cannot [**extend**](https://php-dictionary.readthedocs.io/en/latest/dictionary/extends.ini.html) a [**class**](https://php-dictionary.readthedocs.io/en/latest/dictionary/class.ini.html) | ✅ [**implements**](https://php-dictionary.readthedocs.io/en/latest/dictionary/implements.ini.html)one or multiple [**interfaces**](https://php-dictionary.readthedocs.io/en/latest/dictionary/interface.ini.html) | ✅ [**use**](https://php-dictionary.readthedocs.io/en/latest/dictionary/use.ini.html) one or multiple [**traits**](https://php-dictionary.readthedocs.io/en/latest/dictionary/trait.ini.html) | ❌ Cannot [**extend**](https://php-dictionary.readthedocs.io/en/latest/dictionary/extends.ini.html) another [**enum**](https://php-dictionary.readthedocs.io/en/latest/dictionary/enum.ini.html) | ## Key Takeaways - **Classes** are the most versatile: they can extend a class, implement interfaces, and use traits. - **Interfaces** can only extend other interfaces — nothing else. - **Traits** are like reusable code fragments. They can be used in classes and enums and even use other traits, but cannot stand alone. - **Enums** (introduced in PHP 8.1) behave like special classes: they cannot extend other classes or enums, but they can implement interfaces and use traits. --- # Are bracketless instructions on the way out? Source: https://www.exakat.io/are-bracketless-instructions-on-the-way-out/ # Bracketless instructions PHP control structures, such as foreach, for, while, if/then/else,... are followed by one or several instructions. When there are several instructions, a block needs to be set up, with curly braces. When the following instruction is unique, it is possible to skip the brackets, and only use the instruction. This feature has always been widely discussed among developers. Sometimes, those extra brackets are too much and make the syntax heavier than needed. The rest of the time, they are a matter of consistency and future usage : whoever will update the single instruction into two, will have to add the brackets. We ran some statistics on 2408 Open Source projects to see what is the current situation on that thorny problem. Here are the figures. ## One instruction usage Out of 2408 projects, 2238 were found with at least one suitable control instruction that could be with or without brackets. Basically, 170 projects had not a single one line loop or ifthen. That's 7.1% : it is a marginal case. ## General usage Then, we counted the number of occurrences over the all projects. This means that each for, foreach, while, do...while and if/then was counted. If/then is counted twice, when both the `then` and `else` blocks have only one instruction. In total, 2.5 millions occurences used the brackets (2571914), while 0.4 million used no bracket (432761). All in all, it is 14.4% that are unbracketed. In general, the vast majority of PHP source code are using brackets, even with single instructions. ## Per project usage As a complement, another interesting insight is the coding convention per project. With little impact on PHP itself and more impact on the code writing, choosing to go with or without brackets often turns into a core team value. This pushes the convention toward all with or without brackets. We broke down the previous general figure into the percentage per project : how often are single instructions using brackets inside each project. That way, we can see which projects are systematic with or without brackets, and how the balance is tilted toward one or the other. The precise figures where rounded to make significant groups, and smooth local variations. [![](https://www.exakat.io/wp-content/uploads/2022/07/bracketless-per-20.png)](https://www.exakat.io/wp-content/uploads/2022/07/bracketless-per-20.png)   A very large number of projects (2079) have less than 5% of un-bracketed single instructions. In fact, 1867 of them are totally clean of un-bracketed instructions. On the other end of the spectrum, un-bracketed single instructions are used 95% or more in 145 projects (137 are 100%). In-between are the undecided projects : they don't have a coding convention about it or are not enforcing it strictly (they might be on the way). All together, they are as numerous as the 100% bracketless (145). Practice show a clean lean toward always using brackets with every instruction. Undecided projects and never-bracketing ones are a minority. ## Practice of the bracketless single instruction There are tools to add or remove the brackets : [PHP cs fixer](https://cs.symfony.com/doc/rules/basic/braces.html) and [exakat](https://exakat.readthedocs.io/en/latest/Reference/Cobblers/Structures/AddBracketsToSingleInstructions.html), to name some of them. They may be added to CI pipeline, and enforce coding convention. At the human level, some coding conventions, such as [joomla](https://developer.joomla.org/coding-standards/php-code.html) (see Control Structures (General Code)), [Wordpress](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/#brace-style) or [PSR-12](https://www.php-fig.org/psr/psr-12/#5-control-structures), are explicitely requesting it. They may also have quite some success having it applied, as this report from PHP CodeSniffer suggest : [Control structure defined inline](https://squizlabs.github.io/PHP_CodeSniffer/analysis/#control-structure-defined-inline) ## PHP 9 could set this discussion aside It seems that the PHP community is now well past the point in discussing this feature. It might be a good thing to move it directly into the language, and have PHP directly enforce such a feature. Bracketless single instructions are rare enough to be a mere distraction. Even if they were more common, they lead to pointless discussions. Backward incompatiblity is definitely a concern here, as some projects might have to update their code to be compatible. Although, this mass update is both very safe, with only local modifications, and there are tools ready to do it in a short amount of time. A suggestion, for PHP 8.3 feature list, is this RFC : deprecate the single expression without brackets, for for(), foreach(), while(), do...while(). They would then join switch(), match(), functions and closures, classes and interfaces, etc. And drop it entirely in PHP 9. --- # Infinite loops in PHP Source: https://www.exakat.io/infinite-loops-in-php/ [![Going to the horizon with infinite loops](https://www.exakat.io/wp-content/uploads/2025/07/infinite.320-300x300.jpg)](https://www.exakat.io/wp-content/uploads/2025/07/infinite.320.jpg) # Infinite loops in PHP Sometimes, they are demonized, and sometimes, they are useful. Infinite loops come in a variety of options in PHP, so let's put them neatly on a shelf, with all theirs variations. ## Classic while (true) To make an infinite loop, the most simple is to take a loop, and make it unconditional. The poster child for this is `while (true)`. ## Using do...while(true) Obviously, `do...while` can do the same than `while`, but it is seldom mentionned anywhere. Indeed, `do...while` is roughly used 10 times less than `while`. The main difference is that the loop will be executed at least one, but with infinite loop, what differences does this make? ## With for (;;) Another big star of the infinite loops is the empty `for()`. Here, the loop uses 3 expressions, which are initialization, terminaison and increment. Usually, removing terminaison or increment lead both to infinite loop. In the end `for (;;)` is a lot more readable. ## Using a Generator with foreach() Let's move to `foreach()`. This one is naturally bounded: `foreach()` reads all the values in the provided source. This means that no source build on an array can be infinite: PHP will never build an infinite array before running out of memory. So, we can use a generator. Let's see how this works: To be honest, this is a bit cheating. There is a hidden `while(true)` hidden in the generator, which is actually creating the infinite loop. Let's see if we can do better. ## Using an inifinite Generator with foreach() To get rid of the `while(true)` in the generator, we can rely on our good old friend `yield from`. While `yield` emits one value, `yield from` can do the same from an array, or another generator. We just need to get an infinite number of generators. Or, we can use the same one, and pass it an argument. It is lesser known that generators can take arguments, but this works. Here, the second generator is initialized with the previous last value. This builds an infinite recursion. Compared to the previous examples, it certainly less efficient, as the recursive calls are creation nested execution contexts. We might exhaust the memory before reaching the infinite.... (sic). ## With the PHP native InfiniteIterator The [infiniteIterator](https://www.php.net/manual/en/class.infiniteiterator.php) loops over another iterator, which provides the values. The underlying iterator must be able to rewind, which is to start at the beginning. Here, an array is a good example. ## Infinite foreach() by itself One trick of `foreach()` is that it works on a copy of the initial array. This is a default security to make loops finite. But, `foreach()` also ensures that all the keys of the source array are actually used. So, when the `foreach()` works with references, it actually switches to using the original array, as it may have to updates its values. Then, it runs over all the keys, but now, the keys may be changed by writing again in the same array. The trick is to create only new keys in the source array. And also, to start with one value in the array, at least. Otherwise, PHP skips the whole loop. ## Using goto We could not avoid mentioning `goto` as a great tool to build infinite loops. After all, all the loops above are built on top of a hidden `goto`. Just don't tell anyone I mentionned it, there are goto-haters in the wild. ## Using a Recursive Function Finally, it is possible to skip entirely the loops by relying on recursive functions. Just skip the terminating condition. We already ran into a variation of this, with the recursive generator. It was not the `yield` that was critical, but the recursion. # Infinite loop, the sky is the limit Infinite loops are useful, with indefinite waiting loops, such as event loops. This is when there is no limit to what may happen, so looping again and again is important. In the other cases, it is better to know how to write an infinite loop. Not to use it, but very well to avoid running it in production. --- # A PHP Dictionary Source: https://www.exakat.io/a-php-dictionary/ # A PHP Dictionary In the vast and dynamic ecosystem of PHP development, newcomers often find themselves grappling with a plethora of unfamiliar terms and concepts. From functions and methodologies to frameworks and patterns, navigating the PHP landscape can sometimes feel like learning a new language altogether. That's why why we are introducing a PHP Dictionary. TLDR; go the the [home page of the PHP Dictionary](https://php-dictionary.readthedocs.io/en/latest/) ## A PHP word for each of us The** Dictionary **is a collaborative effort to compile a comprehensive list of common words and concepts used within the PHP community. Its primary goal is to provide clear definitions and explanations for these terms, making them more accessible to newcomers and, sometimes, seasoned developers alike. ## Why do we need a PHP Dictionary? For beginners, learning PHP can be overwhelming, especially when faced with a river of technical jargon and acronyms. With a common resource like the PHP Dictionary, they can significantly reduce the learning curve by reading concise explanations and contextual examples for each term. Even more experienced developers encounter unfamiliar terms from time to time, particularly when reading advanced blog posts. In such cases, having a quick reference like the PHP Dictionary can offer extra references for a better understanding and PHP dedicated resources. The dictionary contains over 940 entries at the moment, and over 1600 external links. This should be sufficient for long winter nights. ## What's in the dictionary? The  Dictionary is an [open-source project](https://github.com/exakat/php-dictionary): this means that anyone can contribute to its development. Contribute by requesting missing words, or suggesting new references and illustrations. Each entry in the dictionary includes: - Definition: A clear and concise explanation of the term's meaning. - Usage: Practical code illustration in real-world scenarios. - References: Links to more in-depth articles, tutorials, or documentation for further exploration. - Connex: Close concepts, with complements or disambiguation. The entries are all manually reviewed, then provided as open source dataset. They are also made available online, for direct reading. ## Contributing to the PHP dictionary Contributing to the dictionary is easy. Simply visit the project's GitHub repository and submit a pull request with your proposed additions or edits. Whether you're adding a new term, refining an existing definition, or providing additional references, your contributions play a crucial role in making the dictionary a valuable resource for the entire community. --- # How to make emojis in PHP Source: https://www.exakat.io/how-to-make-emojis-in-php/ # How to make emojis in PHP You might want to use emojis both in your online comments and your PHP code. The later, mostly to hander the former, although PHP handles emojis gracefully. That are the methods to make emojis in PHP? ## Straight in the code PHP supports code savec in UTF-8. And emojis are also available in that character set so it is possible to use them directly in the code. This method is direct and visual. You may need an emoji encyclopedia, such as [https://emojipedia.org/](https://emojipedia.org/elephant), or [emojis.wiki](https://emojis.wiki/), where copy-paste is a great tool to select any emoji. Yet, this all depends on the underlying text file to be always stored as UTF-8. ## Using Escape Sequences It is possible to make emojis without relying on the UTF-8 support of the editor. PHP accepts escape sequences, which is a special format to represent long UTF-8 characters. All is in ASCII characters, so these emojis are more stable in the code. They are also no WISIWIG. ## Using ext/mbstring mbstring is the PHP extension for Multi-bytes strings. It is installed on many PHP binary, as it is commonly used for UTF-8 manipulation. There is a dedicated function to create the emoji. The codepoint has to be provided as an hexadecimal value, hence the `0x` prefix. In particular, the codepoint is mentionned in emoji or UTF-8 character references. It is also possible to pass the decimal or binary value of the codepoint, but it is less common. ## Using iconv The other PHP extension to convert between encodings is iconv. The first step is to pack the codepoint into a 32 big endian integer, then to convert it from UCS-4 (big endian) to UTF-8. ## Using html_entity_decode() html_entity*_*decode() converts HTML sequences to characters. This is an alternative to using PHP escape-sequences, ## Using a ready-made PHP component There are components that convert method calls, constants or escape sequences to emojis. This makes it easier to handle in case the emojis are dynamically managed, such as automatically added in titles or comments. You can use [emojione/emojione](https://packagist.org/packages/emojione/emojione) or [*spatie/emoji*](https://packagist.org/packages/spatie/emoji) # Five ways to make emojis in PHP These are at least five ways to make emojis in PHP. Also, they may be used in PHP code itself, not just for string manipulations. This may be applicable for mathematical functions (function ∑(...$numbers)) or localisation (class 商品Manager). A lot of fun! --- # 1-2-3 PHP operators Source: https://www.exakat.io/1-2-3-php-operators/ # 1-2-3 PHP operators I learnt my PHP on a European keyboard, and it was always a pain to use some strange symbols. When I moved to Canada, I quickly realized that the chosen symbols were actually very easily accessible on the keyboard: it was just my layout that was less practical. Then, I also realized how these symbols are repeatedly used to make larger ones. There is the single `=` for assignation, then the double `==` for equality and then the triple `===` for identity. It is probably easier to repeat an existing symbol, than to summon a new one, which might be cumbersome to reach on the keyboard. There is a bit of laziness in this pattern. `==` might easily mistaken with `===` (and vice versa) and the mistake might be begnin (or not), but mistaking `=`and `==` is, most of the time, a bug. Here is as selection of 1-2-3 PHP operators, along with their documentation. | 1 | 2 | 3 | | --- | --- | --- | | . | .. | ... | | + | ++ | +++ | | - | -- | --- | | * | ** | *** | | / | // | /// | | > | >> | >>> | | < | << | <<< | | = | == | === | | @ | @@ | @@@ | | ? | ?? | ??? | | : | :: | ::: | | ! | !! | !!! | | $ | $$ | $$$ | ## Dots in PHP - `.` is the operator of [string concatenation](https://www.php.net/manual/en/language.operators.string.php). - `..` is a syntax error. It is not supported in PHP. In other language, it denotes a range between two operators. - `...` is the [ellipsis operator](https://www.php.net/manual/en/language.types.array.php), to spread or collect values out or in an array ## Plus in PHP - `+` is the [addition operator](https://www.php.net/manual/en/language.operators.arithmetic.php), for numbers and arrays. - `++` is the [increment operator](https://www.php.net/manual/en/language.operators.increment.php). It comes in two flavors, pre-increment or post increment. - `+++` is a syntax oddity, where there is no space between an incremented variable and its addition with another value: `$b = $a+++ 1;` is `$b = $a++ + 1;` ## Minus in PHP - `-` is the [substraction operator](https://www.php.net/manual/en/language.operators.arithmetic.php), for numbers only. - `--` is the [decrement operator](https://www.php.net/manual/en/language.operators.increment.php). It comes in two flavors, pre-increment or post increment. - `---` is a syntax oddity, where there is no space between a decremented variable and its substraction with another value: `$b = $a--- 1;` is `$b = $a-- - 1;` ## Star in PHP - `*` is the [multiplication operator](https://www.php.net/manual/en/language.operators.arithmetic.php), for numbers. - `**` is the [power operator](https://www.php.net/manual/en/language.operators.arithmetic.php). It is the multiplication of multiplications, basically. - `***` is not supported. Math has a notion of towers of powers, where `4 *** 4` would be `4 ** (4 ** 4)`. That number is already beyond the integer range, so the operator is probably unuseful. ## Slash in PHP - `/` is the [decimal division operator](https://www.php.net/manual/en/language.operators.arithmetic.php), for numbers. - `//` is a [one line comment](https://www.php.net/manual/en/language.basic-syntax.comments.php). - `///` is a one line comment, starting with a slash. ## Greater than in PHP - `>` is the [greater than comparison operator](https://www.php.net/manual/en/language.operators.comparison.php). - `>>` is the [right bitshift operator](https://www.php.net/manual/en/language.operators.bitwise.php), which moves bits to the right. - `>>>` is not supported. ## Lesser than in PHP - `<` is the [lesser than comparison operator](https://www.php.net/manual/en/language.operators.comparison.php). - `<<` is the [left bitshift operator](https://www.php.net/manual/en/language.operators.bitwise.php), which moves bits to the left. - `<<<` is the start of the Heredoc and Nowdoc sequence. ## Equal in PHP - `=` is the assignation operator, which gives a value to a variable. - `==` is the [comparison operator](https://www.php.net/manual/en/language.operators.comparison.php). Types are adapted before the comparison actually happens. - `===` is the [identity operator](https://www.php.net/manual/en/language.operators.comparison.php), which compares values and their types. ## Question in PHP - `?` is half the [ternary operator](https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary), along with the colon. - `??` is the [null coalesce](https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.coalesce) operator. It is a ternary operator, specialized with null values. - `???` is a [classic comment](https://github.com/search?q=%3F%3F%3F+language%3APHP+&type=code) in the code, when someone was surprised by some code. ## Colon @ in PHP - `:` is the label operator, `label:`, that works with [goto](https://www.php.net/manual/en/control-structures.goto.php). It is also the other half of the [ternary operator](https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary). - `::` is the [scope resolution operator](https://www.php.net/manual/en/language.oop5.paamayim-nekudotayim.php), which reachs data at the class level, and not at the object level. - `:::` is not supported. ## Exclamation ! in PHP - `!` is the [negation operator](https://www.php.net/manual/en/language.operators.logical.php): it turns any value into its boolean opposite. - `!!` is the [negation negation operator](https://www.php.net/manual/en/language.operators.logical.php). It is basically a shorter `(boolean)` cast operator, and a barbarism coming from other languages. - `!!!` is the [negation operator](https://www.php.net/manual/en/language.operators.logical.php), but three times longer to write. ## Arobase @ in PHP - `@` is the [noscream operator](https://www.php.net/manual/en/language.operators.errorcontrol.php), which mutes error reporting on a syntax. - `@@` is the [noscream operator](https://www.php.net/manual/en/language.operators.errorcontrol.php), which mutes error reporting on a error-muted expression. It is in case the code should really not emit errors. - `@@@` is the [noscream operator](https://www.php.net/manual/en/language.operators.errorcontrol.php), which mutes error reporting on a doubly error-muted expression. It is in case the code should really really, and I mean really, not emit errors. # 1-2-3 operators are fun! It is interesting to discover the different meanings of an symbol as it is repeated. It might vary a lot in meaning and usage. It is also interesting to realise that some of these repeated symbol operators are not used yet, and might be promoted to an actual PHP features. Replacing `range(1, 19)` by `1..19` would be a nice new feature for PHP 8.5. We could also dream of `:::`, `|||`, `___` or even quadruples operators `....` or `@@@@`. Well, sometimes, we already have them! --- # Quine in PHP : self displaying code Source: https://www.exakat.io/quine-in-php-self-displaying-code/ ## Quines for PHP A [quine](https://en.wikipedia.org/wiki/Quine_(computing)) is a piece of code that executes to produce itself. This is a self displaying code. The execution of the code outputs the same source code, before it was executed. No changes happened, and one may run it again, to the same result. While this code curiosity has rare direct applications, the challenge leads to interesting cases and manipulations. Here are a few of them. ## Quine with print() The `$code` variable contains the source code as a string, with `%s` as a placeholder for where `$code`will be inserted. The `printf()` function uses `%s` to format the string, and the `39` arguments represent the ASCII value for single quotes `'`. So, `printf($code, 39, $code, 39)` effectively wraps $code in quotes for the output. When executed, this script outputs its own source code exactly as written, fulfilling the quine requirement. This is a straightforward and compact quine, relying on string formatting to replicate itself. ## A Quine Using var_export Another pragmatic approach is to use `var_export()`, which outputs a parseable string representation of a variable. Here’s an example: The `$a` variable holds the source code template with a %s placeholder. `var_export($a, true)`generates a string representation of `$a`, including quotes, which is then inserted into the template using `sprintf()`. When executed, `echo` outputs the entire script, matching the source code. This method is similar to the first but uses `var_export()` to handle the string representation of the code, which can be particularly useful for ensuring the output is a valid PHP string. ## A Quine With a Function and getdefinedvars() Here’s a more creative approach using a function and PHP’s get*defined*vars to access the source code:`` `` The function `q()` uses `get_defined_vars()` to access all variables in its scope, including `$s`, which contains the source code. The `$s` variable is a string that includes the entire script, with `var_export()`used to properly format `$s` within itself. When `q()`is called, it outputs the value of `$s`, which is the source code itself. This quine is more complex because it uses a function and PHP’s introspection capabilities, but it still achieves the same goal. ## A Quine Using File Reading Some quines cheat by reading their own source file, though this is often disallowed in strict quine competitions because it uses external input (the file itself). Still, it’s a common technique in PHP:`` `` **FILE** is a PHP magic constant that gives the full path to the current script.`file_get_contents(__FILE__)` reads the script’s source code and echo outputs it. This outputs the exact source code, but it’s considered a “cheating quine” because it relies on reading the file rather than generating the code internally. ## Quine with PHP native functions This is a quine by Tim Bond. It is based on reading the source of the quine in a file, but without `file_get_contents()`, and with a twist. The file is read with `highlight_file()`, but cleaned into text with `strip_tags()` and `html_entity_decode()`. This is quite a detour to read the source, but it works flawlessly. ## Quine and chr() A nice and rather unreadable quine from [Zuchmanski](https://gist.github.com/zuchmanski/953463). This quine is based on the ASCII representation of the code, which is turned into actual characters with the `chr()` function, and then glued together with the concatenation. With a bit of ASCII knowledge, you will find the characters to write `a` (aka, 97) or `'` (39, as seen previously). The code is injected in a variable, then echo-ed to the output again later. ## Quine and chr() Here is the quine that waited for years to be published, by [Benoit Viguier](https://phpc.social/@b_viguier). It is based on arrow functions: look among the pile of punctuations, the arrow function (`fn($s)`) is defined, then immediately called upon itself, with the code in a string. The quotes are hidden with a character sequence `\x27`, and there is not a single semi colon in sight. Quite a feat for the PHP parser! ## PHP Quine There are many ways to create a quine in PHP. `echo` or `print` for the display, and a duplication of the code in a variable which is later displayed. The result is quite fascinating: it is a fixed point, and it is guaranteed to exists, thanks to PHP being a language Turing complete! Now, where will we use these in an actual application? --- # PHP parentheses usage Source: https://www.exakat.io/php-parentheses-usage/ # The Required, The Optional, The Useless, and The Weird: of In PHP, parentheses `()` aren't just punctuation : they play different roles depending on where and how they're used. Some are compulsory, others are optional, and a few are just useless clutter. And then, there’s the weird one: (cast). Let’s review all PHP parentheses usages down. ## Compulsory Parentheses The compulsory parentheses are the ones PHP needs to make sense of the code. No one can omit them, and the source won't even compile without them, so they are compulsory. ### Function definitions and method calls And vice-versa, obviously. ### Control structures like if, while, switch, match Baked into the PHP parser, nothing much to add there. ## Optional Parentheses Sometimes, parentheses are allowed but not necessary, so it is up to the human in the room to decide if it makes the code clearer, or valid. ### Grouping expressions This often applies to math expression, and more generally, operators. One may even wonder, if operators evolved into functions, they probably brought the parentheses with them. Such parentheses are optional, as the expression may be refactored without them. Not that it would help in anything, it's just possible. ### Operator precedence Some expressions are not the same, with and without parentheses, thanks to operators precedence. Both are valid, and one is definitely more popular, and in the end, it is a choice. Inverting the operators helps to make this more obvious, if not more readable. ### Readability All expressions use the operators precedence rules, but they are hard to read: at least, for some of us. Then, adding parentheses makes it more readable. ### The equal sign Actually, one operator that often needs clarification with parentheses is the `=` sign. An assignation has one of the lowest precedence among operators, and it never expands to the next expression, but rather only capture the next result. Of course, it is also a confusing syntax from the beginning: just try to guess the execution order in such expressions. ## Parentheses with new: inside `new` operator works with or without parentheses. When the constructor requires any argument, the parentheses are compulsory. When the constructor requires no arguments, then the parentheses become optional. They are usually recommended, as a common best practise. ## Parentheses with new: outside `new` operator produces a new object. Sometimes, that object needs to be accessed immediately, for a method call, or a property read. At that point, the parentheses used to be compulsory. When the constructor requires no arguments, then the parentheses become optional. They are usually recommended, as a common best practise. Since PHP 8.4, it is possible to use the newly created object immediately, without parentheses. Or, to be precise, only with the constructor parentheses (see section just before). ## Useless Parentheses There are places where parentheses are thought to be useful, but they are not. This is the case with all PHP language constructs, such as `echo`, `print`, `include`, etc. Here, the parentheses are accepted, like any other expression, as the single argument for the language construct. Parentheses forces the inner expression to be resolved, and passes the results to the outer expression. First, this means that nesting parenthesis is completely arbitrary, and futile. Secondly, it hides the fact that `echo` accepts multiple arguments, separated with a comma. In fact, to make the one-argument `echo` work, the strings must be concatenated first, to be useful. `echo` is the only one with that hidden feature. `print`, `include` only accept one argument, and `empty` works only with parentheses. ## The Weird Parentheses: casting, constants and functioncall Finally, there is an odd bug in the parsing engine, that catches anything that resemble a cast, such as `(float)`, `(int)`... etc. in normal syntax. So, the recommendation here is to avoid using `cast`names as global constants: int, float, array, string, [void](https://wiki.php.net/rfc/marking_return_value_as_important). Luckily, it is not a common behavior. ## PHP parentheses usage Parentheses are quite versatile in PHP, ranging from compulsory to down right useless or buggy. It is still a very convenient tool, in particular to manage priorities between operators. When in doubt, just use them. --- # Speeding up ksort() Source: https://www.exakat.io/speed-up-ksort/ # Speed up ksort() `[ksort()](https://www.php.net/manual/en/function.ksort.php)`is the PHP native function that sorts an array by its keys, while maintaining the relationship with the values. It has been in the language since the beginning (last millenium!), and yet, sometimes, it is interesting to ask again some old questions: can I speed up ksort()? Let's see. ## ksort(), sorting by keys To start with, let's review what `ksort()` does. Then, here is how it would work if PHP did not provide this native function. The surprising part of this piece of magic is that ... there is no actual sorting involved. No comparison, no recursive call, nothing. How is it possible that `$sorted` is sorted in the end? The sorting actually happens at the array creation. `[array_pad()](https://www.php.net/manual/en/function.array-pad.php)`fills an array with empty strings, up to `count($array)`. Incidentally, its keys are automatically in the right order, from 0 to `count($array)`. This is already half the job done! The only remaining task is to assign the right value to the right key. This is a job for the loop: PHP finds the existing key in the array, and assigns it the value from the original array. The keys were sorted, they stay sorted after the loop. Et voila! ## ksort() versus foreach(), the speed test No need to take our pocket watch out, `ksort()` is faster than the `foreach()` loop + `array_pad()`combinaison. The difference is quite gigantic: `foreach()` is 6 times slower than `ksort()`. So, speed wise, just keep using `ksort()`. ## ksort() versus the loop, with a modification To keep things interesting, let's upgrade our speed test with a modification on the values. This time, we'll sort the keys, AND we'll apply a modification to the values. To keep the load out of the way, we'll use the identity function: this is the function `foo()`, that returns the incoming argument. It does nothing, except represents the minimum task to accomplish. Let's see how the codes are evolving: ksort() is now complemented with its own `foreach()` loop. It could also have been a call to `[array_map()](https://www.php.net/manual/en/function.array-map.php)`, though it is a bit slower. The alternative is lightly updated with an extra call to `foo()` in the loop. The interesting result lies in the speed test : the `foreach() + array_pad()` is now 15% faster than the `ksort() + loop`. ## Sorting without using any comparison The secret of this situation lies in the internal features of the PHP engine. `ksort()` takes the keys two by two, and applies a comparison to rearrange the keys and the values in the right order. On the other hand, `array_pad()` produces the keys immediately in the right order: this is fast. Then, during the loop, the keys are not moved, but rather, the values's new address is calculated by hashing the key: that computation is straightforward, and then, PHP stores the value in a position. This is much less work than sorting keys! ## Other sortings? The trick here works as long as `array_pad()` can generate the expected sorted order. The post started with integer indices, from 0 to 5, and it is easy to generate such keys in the right order. Of course, `array_pad()` is not the only option available. These other starting options allows for sorting from 0 or an arbitrary offset (with `[array_fill()](https://www.php.net/manual/en/function.array-fill.php)`. The alphabet is also possible, since PHP is able to generate it quite efficiently, with the `[range('a', 'z')](https://www.php.net/manual/en/function.range.php)`function (it would not work with greek alphabet, though). By extension, any already sorted array may act as a starting point, as long as it is easy to get. On the other hand, sorting by country names or winner of the last Tour de France might make the initial array difficult to collect, without sorting it with `ksort()`. ## Raw power versus combined operations The origin of this algorithm came from reading data from a source, that would not provide the keys in the right order. That lead to the usage of `ksort()`, until the need for last minut cleanup on the data appeared. At that point, the key-sort-only focus of `ksort()`had to be completed with a second loop. After all, `ksort()` is a loop by itself, although well hidden. The best way to optimize two adjacent loops is to merge them: this means only one pass and less overhead. But how to sort keys in one pass, then store the result of the new value? The only available feature is the fact that PHP doesn't change the keys when processing an array with `foreach()` and that accessing indices in an array hashed, and very efficient. As usual, speed tests were needed to ensure the result was useful. ## Speed up ksort() Before going down that rabbit hole, I would not have said that `ksort()` may be beaten, but it seems that it was the case. As usual, it depends on context and various local advantages: it pays to know your environment. The final result of 15% speed up is quite surprising, but it is also a micro-optimisation. It is more important to understand the underlying principle of PHP engine's features, than to apply it everywhere. --- # PHP error messages encyclopedia Source: https://www.exakat.io/php-error-messages/ # PHP Error Messages Encyclopedia If you’ve ever coded, you’ve likely encountered cryptic error messages that left you scratching your head. Whether it's a syntax error, an unexpected function call issue, or a deprecated feature warning, debugging can be a frustrating experience: this also happens with PHP. That’s where the **[PHP Error Message Encyclopedia](https://php-errors.readthedocs.io/en/latest/) (PEME)** comes in. This extensive reference provides clear explanations for over 500 PHP error messages, helping developers of all levels understand what went wrong and how to fix it efficiently. ## What to expect in the PHP Error Message Encyclopedia? The PHP Error Message Encyclopedia is a well-organized reference guide that documents various PHP errors, warnings, and notices across different PHP versions. Each error entry includes: - A description of the error message - The PHP versions in which it occurs - Possible causes of the error - Recommended solutions to fix or prevent it - Extra links with complementary articles about the features - Link to the [PHP dictionary](https://php-dictionary.readthedocs.io/en/latest/) This resource is especially valuable because PHP errors can sometimes be vague or misleading. Instead of spending hours searching forums and Stack Overflow, you can find a clear and concise explanation right here. ## Why You Should Use The PHP Error Messages Encyclopedia - Comprehensive Coverage : with over 500 documented error messages, you’ll likely find an explanation for whatever PHP issue you’re facing. And it's growing everyday. - Version-Specific Guidance : since PHP evolves over time, some errors are version-dependent. This guide helps you understand and anticipate which versions are affected and how updates impact your code. - Time-Saving Debugging : Instead of sifting through various sources, get straight to the point with structured, accurate solutions - Improved Code Quality : Understanding errors means you can write better, more robust PHP code while avoiding common pitfalls. - Window To The Future : upcoming PHP error messages are already here, so you can get ready for them! ## Common Errors Covered Some of the most frequently encountered PHP errors explained in the encyclopedia include: - **[Array and string offset access syntax with curly braces is deprecated](https://php-errors.readthedocs.io/en/latest/messages/array-and-string-offset-access-syntax-with-curly-braces-is-deprecated.html)** - **[Only variables can be passed by reference](https://php-errors.readthedocs.io/en/latest/messages/only-variables-can-be-passed-by-reference.html)** - **[syntax error, unexpected ‘::’ (T*PAAMAYIM*NEKUDOTAYIM), expecting ‘;’ or ‘,’](https://php-errors.readthedocs.io/en/latest/messages/syntax-error%2C-unexpected-%27%3A%3A%27-%28t_paamayim_nekudotayim%29%2C-expecting-%27%3B%27-or-%27%2C%27.html)** - **[Filename cannot be empty](https://php-errors.readthedocs.io/en/latest/messages/filename-cannot-be-empty.html)** - [**Trait "%s" not found**](https://php-errors.readthedocs.io/en/latest/messages/trait-%22%25s%22-not-found.html) ## How to Get Started The **[PHP Error Message Encyclopedia](https://php-errors.readthedocs.io/en/latest/) (PEME)** is freely available online at [https://php-errors.readthedocs.io]. Whether you're a beginner learning the ropes or an experienced developer troubleshooting complex issues, this guide is an invaluable addition to your toolkit. Bookmark it, share it with your team, and make debugging a little less painful! --- # Changed Behaviors in PHP Source: https://www.exakat.io/changed-behavior-in-php/ ## The Ultimate Guide to Navigating PHP Features Updates As PHP evolves, so do the language’s features, functionalities. And even the nuances in its behaviors. Yes, there are changed behaviors in PHP. They range from syntax changes to change in behavior of certain functions. And it can be overwhelming to keep track of how each new version of PHP might impact your code. This is the grouning reason for "**[Changed Behavior in PHP](https://php-changed-behaviors.readthedocs.io/en/latest/)**": it is the reference of features that are changing from one PHP version to the next. With clear explanations, illustrations, and actionable recommendations, this encyclopedia is an essential resource. It is written for PHP developers aiming to quickly understand why the code was working yesterday, and not today anymore. ## What to Expect in the Changed Behavior in PHP Guide - Detailed description of the change: the code has not changed, but the result is different. - The version of PHP when it happens, may it be deprecation or complete removal - Suggestions to handle the situation, either to get ready before the change, or to fix the impact of it - Possible related error messages: sometimes, they are misleading - Other resources that go deeper on that subject - Collection of related other changed behaviors ## Why Use the guide of Changed Behaviors in PHP? This guide is made to help identify possible PHP evolutions that are impacting existing code, and speed up resolutions of theses problems. It may also be used as a technology watch, to anticipate upcoming changes that will make the code not working on the next versions. It also serves as a common knowledge repository: when you[ run into a surprising](https://github.com/dseguy/changedBehavior/issues) change in PHP, report it here, as it will help future fellow developers to avoid it. It may also serve as a base for static analysis developers, to strengthen code. ## Contributing to the Changed Behavior Guide You can share your experience with changed features, via the **[project’s GitHub repository](https://github.com/dseguy/changedBehavior/issues)**: submit a pull request with your proposed additions or edits: new changed behaviors, complementary illustrations or external resources. --- # Prevent multiple PHP scripts at the same time Source: https://www.exakat.io/prevent-multiple-php-scripts-at-the-same-time/ ## Prevent multiple PHP scripts at the same time Like everything, it all started from a simple problem : how to prevent multiple PHP scripts at the same time. And turned into an odyssey of learning, full of evil traps and inglorious victories. In the end, it works, that's the most satisfying and it possibly matters to noone except me. But « the way is the goal », as said Confucius, so, I decided to share the various findings. ## The origin Exakat runs in command line, and it uses a graph database. The database is central to the processing, and it is crucial to avoid running several scripts at the same time : they will write over each other. So, the problem is simple : preventing several instances to run at the same time, on the database. In commandline, there is no web server that may serve as common place between scripts, sharing some memory and implementing a locking system. It requires to use another common ground : the system. ## Using file-lock Files could play that role. PHP offers flock(), in the standard functions, that implements a locking mechanism at the file level. This is convenient, and has been adapted to all platforms. The downside is that the [flock](http://www.php.net/flock)() call waits until any previous lock has been released. This is not practical to warn a user of concurrent usage. ## Using file as lock Creating a file that could serve as a lock is a first-idea solution : the first script creates the file, then any subsequent script finds the file and abort operations. It's a collaborative system. The solution is simple. However, there is a 'race condition' : between the call to file_exists() and the fopen(), however small, there is time for another script to create the file too. This is also known as TOCTOU : 'time of check, time of use' problem. Granted, the time window must be a few milliseconds at best, but still. If that window of uncertainty is not acceptable, there is an alternative : using directories. Files may be opened by two different processes, which happily overwrite each other. On the other hand, directories don't have such problems : they may be created and removed, but they can't be written. So, the best way to check the presence of a directory is to create one. If the creation fails, then the directory is already there. If the creation succeed, then, the directory was not there. This is a bit like merging [file_exists](http://www.php.net/file_exists)() and [fopen](http://www.php.net/file)(). This time, no TOCTOU, no race condition, and a light impact on the file system. So, we may move to the next question : what happens in case of crash ? The previous solution relies on the creation AND suppression of the directory, when the script reaches the end. In case of a crash or interruption, this brilliant system will be left with a lot of garbage, including the lock directory. PHP is clever at cleaning such garbage, but the directory is out of its reach : in fact, this is wanted. So, when we'll restart the process, we'll hit a wall : « another script is already running ». ## Crash-proof locking So, crash-proof is now the new challenge. We need a system that cleans the garbage in case of crash, just like PHP does for everything else. [register_shutdown_function](http://www.php.net/register_shutdown_function)() could cover the cases of [exit](http://www.php.net/exit)(), die() and other exceptions in the code code, but it wouldn't be enough for a crash or an interruption. For those special conditions, we need something that is cleaned or released, even if PHP itself crashes. PHP does a good job at cleaning after itself, so there is actually plethora of solutions. For example, there is [tmpfile](http://www.php.net/tmpfile)(), which opens a temporary file. This can be used for any file manipulation, and the file will be removed as soon as the script terminates (though, I suspect a crash would leave some files in the tmp folder, but I may be too suspicious). Tmpfile() is not usable for locking, since it only give access to the file pointer and not to the name of the file in the system. Without such name, there is no way to coordinate several instances. ## Semaphore locking Another tool that is automatically released are semaphore. This is one of the core PHP extension, activated with a simple –enable-sysvsem. It works simply like that : [ftok](http://www.php.net/ftok)() converts a path and one character into an integer. __FILE__ is a good candidate, and any character is good too : they are handy when you need various locks on the same path. Then sem_get() prepares the semaphore. Therer are actually 4 arguments, and the fourth, omitted because defaulted, is aptly named '$auto-release' : the semaphore is automatically released at the end of the script, whatever its fate. Then, [sem_acquire](http://www.php.net/sem_aquire)() does the hard work of obtaining this semaphore. The second argument is '$nowait' : without it, it will wait indefinitely, but with it, it returns immediately. In the example, the final [sem_release](http://www.php.net/sem_release)() is actually too much. Semaphore were made on purpose to set up some locking system, and they are the most adapted solution. ## Too much compilation for Docker The next snag I hit with this is when creating the Docker file. On a default php:7.1-cli image, the semaphore extension is not compiled. Even though it is a core and --enable extension, this is not available by default. So, I added the semaphore extension the PHP on the docker with the ad-hoc scripts and the size of the docker image doubled. Adding 400Mb, just to make four calls to sem_* and [ftok](http://www.php.net/ftok)() was a bit too much, so I started again the search for a light solution : one that could fit in a slim PHP. ## Socket to lock Without semaphore and files, what could be the next resource that PHP cleans automatically when it crashes ? Sockets. If PHP opens a socket and then crashes, the socket is closed. The idea is now to open a socket on a port, and if this port is already opened, then it is locked. PHP has a lot of possibilities to open sockets, with socket_bind() or fsockopen() : both are in core, but not in php:7.1-cli. One extension that can't be removed is stream, and there is a good function there : [stream_socket_server](http://www.php.net/stream_socket_server)(). ## Finally, streams and sockets This one is used to build a local server, and reserving a port. It uses an IP (0.0.0.0 will do) and an arbitrary port. We may avoid anything below 1024, and use one that is not reserved already (3306 for MySQL, 7474 for [Neo4j](http://www.neo4j.org/), etc.) : there are a lot of them. [fclose](http://www.php.net/fclose)() is actually the closing function for a stream socket, so whenever we can, we close cleanly. We can register that for shutdown, and when PHP crashes, the socket is automatically terminated and removed. The option STREAM_SERVER_BIND is used, as it does the port reservation, but do not listen : we don't actually need to do any networking, so let's not do too much. Since stream_socket_server() report an error in case the port is not available, we have to hide it with @, or [error_reporting](http://www.php.net/error_reporting)(0). I hate using @, as it is slow and ugly and it kills kitten. It is required here, since hitting an error is part of the schema. So, I have to swallow my pride, and keep it. ## Epilogue This whole odyssey wouldn't be finished without the final boss, the oger that viciously hit you when you least expect it, and requires great efforts to vanquish. Yet, it was waiting. After preparing the above solution, I felt satisfied and moved the code in the Exakat engine. The refactoring went fast, as it was simple enough. And I immediately started to to test on several pieces of code : the first run went well, even when trying to start other analysis at various stage of the run. The second analysis stopped dead cold : « Another process is already running ». That message seemed quite wrong, since there was the only one analysis running. I waited a bit, expecting the port to be released with some delay, but that was wrong. Port are a bit more difficult to check than files : netstat helped. And lsof too : $ lsof -i :7500 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 40412 dams 3u IPv4 0xee75f0ba12d95641 0t0 UDP *:7500 So, the last analysis, with PID of 40412, actually kept the socket open. Killing the process solved it, but, of course, the problem came back at the next analysis. Something was not releasing the process. And, for some reason, it was « java » that hold it hostage. Now, that was strange ! ## The opened port was given to another process In fact, « java » is part of exakat, since neo4j runs on Java. So, the culprit was not a total stranger. In fact, it is pretty simple : when exakat starts executing, it often restarts neo4j, to clean the database for the new tokens. It is faster to stop neo4j, remove the data dir, restart than to query-delete all the nodes. Exec() is used to do all of that. Now, starting neo4j with [exec()](http://www.php.net/exec) makes neo4j a child-process of the current process. When the latter dies or finishes, it releases all the resources, but it passes ownership of the opened port to its child. Neo4j is a database, that often stays on, even after the end of the analysis. And it is run by java. So, once exakat finishes, instead of releasing the socket, it gives it to neo4j, which stays on and keeps it opened. This prevent the next exakat to reserve it. Finally, the problem resided in the link between the current process and the neo4j child process. If exakat opens the socket first, then restart neo4j, then the two are linked and neo4j will inherit the open socket. But if neo4j is restarted before the socket is open, then the link is not established, and the socket will be released when the scripts finished, while the database stays on. Once the order of priorities has been defined, it was easy to fix this in the code. ## Prevent multiple PHP scripts at the same time In the end, this was a lot of learning in one afternoon, and it finished well. - Files are bad for locking - [flock()](http://www.php.net/flock) is good, if waiting is not a problem (good for scripts, not for users) - directories are a poor man's solution, but quite handy for a quick fix - Semaphore is the cleanest solution, if you can affort ext/sysvsem - sockets is quite clean, as long as you don't use [exec()](http://www.php.net/exec) - Other solutions may be available and were not analyzed because of lack of need : open a transaction on a database and rollback in case of crash ; use a record or system-wide variables. As usual, when working, the solution was far from the initial problem. But there was learning at all stages, even with the final boss. All this afternoon of work is now included in a few lines of code, ready for the 0.9.5 version of exakat. Does the result matter to you ? Probably not. Does the learning helped you ? Hopefully yes. Next time you face such hardship, share it ! --- # Adoption of PHP 8 attributes in 2022 Source: https://www.exakat.io/adoption-of-php-8-attributes-in-2022/ # Adoption of PHP 8 attributes in 2022 PHP 8 introduced the attributes, in lieu of the phpdoc comments. They may be[![](https://www.exakat.io/wp-content/uploads/2022/05/printing.320-300x289.png)](https://www.exakat.io/wp-content/uploads/2022/05/printing.320.png) added to classes, methods, functions, parameters, properties and class constants and provide a PHP-way to write custom configurations. This is a great addition for the static analysis tools, and PHP in general. 18 months later, how are the attributes doing? Let's take a look in the open source world, where the code may be publicly audited. ## Code penetration Out of 535 open source project tested, 83 were sporting one or more attributes. This makes about project a project out of six. This is a good start, and not tidal wave. For comparison, the spaceship operator <=> is currently adopted by 6%, and started many years ago. ## Popular attributes Here are the most popular attributes, in order of usage occurrences : - ReturnTypeWillChange - Pure - Attribute - ArrayShape - ExpectedValues - Deprecated - Route - SensitiveParameter - Dependency - Immutable Note : twelve more attributes that were found once only were skipped here. Also, actual numbers are not providing any significant meaning. Only the relative order is useful. ## PHP attributes In PHP 8.1, there is only one attribute (besides the Attribute attribute, used to design classes as attributes) : [`ReturnTypeWillChange`](https://www.php.net/manual/en/migration81.incompatible.php#migration81.incompatible.core.type-compatibility-internal). This attribute mutes some warning during inheritance validation. To keep code compatible with PHP 8.1, it is compulsory. That explains the first place of this attribute in the ranking. ## PHP 8.2 attributes One interesting pick in the list is `SensitiveParameter`. It is in fact a PHP attribute, but a PHP 8.2 one. It is in the current code, along with `SensitiveParameterValue`. It is part of an adopted RFC, by Tim Düsterhus : `[PHP RFC: Redacting parameters in back traces](https://wiki.php.net/rfc/redact_parameters_in_back_traces)`. Their goal is to hide the content of the variable when displaying information with the debug function of PHP, such as `[debug_print_backtrace()](https://www.php.net/manual/fr/function.debug-print-backtrace.php)`. Another upcoming attribute is `AllowDynamicProperties`, which, as its name states, will be used to allow dynamic properties in chosen classes. That will make five native PHP attributes in PHP 8.2. ## PHPstorm attributes [`JetBrains`](https://www.jetbrains.com/) published a corpus of [8 attributes](https://github.com/JetBrains/phpstorm-attributes) to put in the code and help their static analysis tool provide better results. This is the case for `Immutable`, `Deprecated`, `ArrayShape`, `ExpectedValues` and `Pure`. Those attributes are used with the PHPStorm IDE, with the direct effect to make false positives go away. It shows the importance of publishing attributes : as those attributes are being adopted in the codes, they should be adopted by static analysis tools, and reduce the number of options in the code. ## Symfony attributes `[Route](https://symfony.com/blog/new-in-symfony-5-2-php-8-attributes)` is a Symfony attribute. It allows the configuration of a route, directly in the controller. ## Navarr Attributes [`Dependency`](https://github.com/navarr/attribute-dependency) is an attribute, defined in the [Navarr/Attribute-dependency](https://packagist.org/packages/navarr/attribute-dependency) package, to handle dependencies. # Future of the Attributes Attributes are definitely in the adoption phase. The main publishers of attributes are PHP itself, for its own usage, static analysis editors and frameworks editors. Even at that, they are rather courageous pionneers and innovators. This quick survey collected ten attributes with significant usage. As this list grows, the need for a common dictionary with the attributes and their usage will emerge. There are quite a lot of options to decorate classes and methods, and it will be nice to keep this population under control. --- # The very useful variadic argument Source: https://www.exakat.io/the-very-useful-variadic-argument/ # The very useful variadic argument One discreet update of PHP 8.1 is the upgrade of PHP static variables behavior in classes : [Static variables in methods inheritance](https://wiki.php.net/rfc/static_variable_inheritance). Let's review what are [static variables](https://www.php.net/manual/en/language.variables.scope.php#language.variables.scope.static) in PHP, why they were problematic so far, and strategies to upgrade the code to remove this problem. ## PHP static variables While static properties and methods are quite known, static variables are more of a secret. They look like this in the code : Contrary to mundane variables, the static variable will not die at the end of the function call, and stays alive until the next call. This is how the function in the example keep returning `$count`, after incrementation, and it displays a different value each time. Static variables are convenient when you need to keep some value across calls, and this value is only useful to this function. ## Inherited static variables The static variables are available in methods, though they used to behave quite surprinsingly. They are actually created for each class from where they are called. They might end up as multiple instances, even though, they are only defined once. See it from this example by Nikita Popov : The static variable is used in the A and B classes, yet the method is inherited so it is actually the same code which is called. Until PHP 8.0, there would be a distinct static variable depending on which class is called. This was fixed in PHP 8.1, where the behavior is now the same than a static property : the variables are now shared between the methods `B::counter()` and `A::counter`, and both are now the same. This bug will bite when a method of a class is inherited by another method, and also uses static variables. As we mentionned initially, this is quite rare, and calling a static method with different classes is also quite rare, so this might be a rather infrequent problem. Which means that it will be quite hard to understand when it arise. So, there is a dedicated Exakat rule called [Inherited Static Variable](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#inherited-static-variable) to detect them in your code. This is part of the [Migration PHP 8.1](https://exakat.readthedocs.io/en/latest/Reference/Rulesets.html#compatibilityphp81) ruleset. ## Upgrading static variables to static properties One of the main advantage of static variables is that they are invisible from the outside. The method acts as a black box from the outside. This is usually nice in the initial version of the code, until the moment the need to watch the value itself emerges. Then, one is dependant on the method itself to provide a way to access it, which is never convenient. So, one easy way to upgrade a static variable is to make it a property. The property is now stored outside the method, and inside the class but readily available across multiple calls. It is also available for extra forensic and sollicitations, like with a simple getter. Note that this solution moves the static variable to a normal property : static variable to static property is possible. Here, it would share the counter across ALL objects, which is not a desirable effect. ## Injecting the static variable Another option is to move the static variable as an injection, and have it always provided as an argument to the method. Basically, instead of storing the variable in the current object, it is now stored in the calling context, which has the responsability to create it, and provided it each time. This example is still the initial counter example, so it is a bit simplistic, and it also requires a reference to avoid loosing the value in the method. It would have been quite different with a Counter object. The main inconvenient of this approach is the necessity for the calling context (here global) to maintain this object until completion. ## One less bug in my code PHP 8.1 brought quite a nice list of evolution and bug fixes : the inherited static variables is one of them. It is quite rare, but also quite puzzling. The alternative solution is also simple enough that you may actually prepare your code by auditing it for compatibility PHP 8.1 with exakat, and then, decide how to make this upgrade. Even if this upgrade is not your next step, it will make the code safer to upgrade later. Happy PHP auditing! --- # Weekly report Source: https://www.exakat.io/weekly-report/ # Weekly report The [Weekly report](https://exakat.readthedocs.io/en/latest/Reference/Reports.html#report-weekly) is a special audit report. It sounds weird initially, to get a report per week, yet this is exactly what it does. Each week, a new report is built, based on a selection of different rules. And this is helps a lot with code quality! ## A different set every week Each week, a set of different rules are extracted, based on the [Analyze](https://exakat.readthedocs.io/en/latest/Reference/Rulesets.html#analyze) and [Suggestions](https://exakat.readthedocs.io/en/latest/Reference/Rulesets.html#suggestions) rulesets. This is a pot-pourri of code modernisation and bug squashing, in a short list, with a pinch of randomness to spice it up. The list is short, so that it is possible to focus on it over one week and finish all issues. This gives times to read those issues, understand and spend a reasonable amount of time fixing them, without making it too large of a task. Usually, reviewing them all is an accessible goal, so you can dive immediately. And remember, that next week, the list will change. ## A set of rules for everyone One elegant aspect of the Weekly report is that this selection is the same for everyone. You may be working on your code, updating your codebase, while your friends and colleagues are using the same rules, on their own code base. So the refactoring focus is the same for everyone, even when the code is not. Which means that, even working separately, it is possible to share and discuss possible solutions, and tips to deal with a specific situation. There is no need to present in details a code situation, or a rule : they are this week's focus. Time to discuss it and collaborate. ## Previous and even future The Exakat Weekly report also features the previous weeks worth of reviews, and, also, the next one. That way, you can review old classics, or even, get yourself ready for next week. Just be warned : next week selection may change anytime until it is next week!       ## Running the report The [Weekly](https://exakat.readthedocs.io/en/latest/Reference/Reports.html#report-weekly) report is build with the following commands : `php exakat.phar init -p my-project -R $URL php exakat.phar project -p my-project php exakat.phar report -p my-project --format Weekly` Then, the weekly report is available in the `projects/my-projects/weekly`report. Open it with an HTML browser. If you have already run an audit, you can skip the first two steps. [Installation instructions](https://exakat.readthedocs.io/en/latest/Administrator/Installation.html) are in the manual. When you adapt the configuration of the audit, make sure that [Analyze](https://exakat.readthedocs.io/en/latest/Reference/Rulesets.html#analyze) and [Suggestions](https://exakat.readthedocs.io/en/latest/Reference/Rulesets.html#suggestions) are run. ## See you there The Weekly report is used to review Exakat itself, a bit every week. You can [join](https://phpnl.slack.com/join/shared_invite/zt-6l6e24rq-qLQEvHsR8A2VATDB0XdnRg#/shared-invite/email) the [PHPNL](https://phpnl.nl/) #exakat channel, and discuss it. --- # When does PHP check for Fatal Error? Source: https://www.exakat.io/when-does-php-check-for-fatal-error/ # Late, later and latest PHP checks PHP applies various checks when processing source code. When those checks fail, they might generate a `Fatal error`and stop execution. They are the bane of production servers, and every PHP developper tries to avoid them as much as possible (unlike static code analyser authors which try to generate them). The goal is 'no error on production'. So, when does PHP check for Fatal Error? Yet, this legendary goal is still eluding us. PHP is far from being exception free, like [Pony language](https://www.ponylang.io/discover/#what-is-pony) or [Agda](https://wiki.portal.chalmers.se/agda/pmwiki.php). Those languages come with a full proof of their code, or even a termination checker. It is both scary and enlightening! One root for the late discovery of any error is the moment when PHP actually applies a check and discovers the problem. So, in order to realize how some errors show up only during execution, let's review a few of PHP phases of execution and code linting. ## Grammar phase The first phase is when `php -l` is used in command line. This phase is only available in command line. No PHP function offers this feature, and the closest we have is eval(), which will run actually the code immediately, and token*get*all(), which will leave us with the token list. PHP reads the source file, breaks it into tokens, and tries to makes sense of them. For that, it uses a grammar file, hence this phase name, although it is quite unofficial. Note that only one file is being checked at that moment. When PHP checks for the grammar of the tokens, it is the moment where you discover an embarassing typo that produces a syntax error. The good part of this is that it is detected immediately, and PHP won't run anything with such an error. And unless this is commited, it is not a problem. Just don't commit PHP syntax errors. ## Linting phase Then, right after this, linting phase kicks in, and PHP applies the first checking rules. Indeed, even if the code has a correct syntax, it may run into some higher structuring rules that prevent it from running. It is akin to the following sentence : `The stone eats the stone`. It is syntactly correct, but it doesn't make much sense, except in rare situations. For example, PHP has to check that a function is declared only once. Since functions may only be declared once, it is not possible to create a second one with the same name. There, it is not a syntax error, but a `Fatal error`. ## Linting consistence This is nice, and it prevents unsuspecting copy/paste or major typos to creep up in the code. Also, this also comes with a caveat. The following and very similar code does lint, yet doesn't execute as one would expect : This means that linting doesn't check classes names; it also doesn't check any other naming conflict with global contants, interfaces, traits or enumerations. All those are resolved at execution time. Which means that this code has now to be executed. ## Execution phase At execution time, PHP is done parsing and linting locally any files. Yet, parsing and linting are not finished : during execution time, one may include extra files, or eval() extra code. So, this will go through again through the parse / lint phase for the file, then it will add an extra layer of checks when merging both code bases. In the following example, two files declare the same class. Both lint individually, and the conflict arises at execution time only. ## Execution path Execution phase linting is all the more difficult to comprehend that it actually depends on the execution path. Which expressions are called, and in which order. This may have a huge impact on those naming conflicts, since some path may be very rare. For example, the above code may be rewritten with an obvious (and not recommended) reduction in bug frequency, just like this : The error will only appear when the include is called, which is once every ten thousand times. So, this is rare, but it will eventually happen. ## Latest PHP checks If the last exemple is too academic, let's take a look at this one. This time, the linting process identifies immediately the discrepancy between the argument typehint and the literal string `"abc"`. As we have seen it, we can postpone this by using intermediate structures, like a constant. By making the default value, we actually pushed the checking to much later, in the execution phase. This error will be dectected, after two conditions are fulfilled : - foo() is actually called (aka, no call, no check) - foo() is called without parameter. ## The curse of the optimization of the inclusion Two roots appears from this example : first, the curse of inclusion and second the effect of optimisation. Include() and its related cousin autoload(), means that the PHP code is broken in multiple files, which are only merged together at execution time. Thus, some tests cannot be run until the code is executed. Even when inlining all PHP code into one file, some of the checks would be postponed to execution, as with the constant example. Optimisation is the second root for these late reports. PHP has a lean engine, with high optimisation toward execution. In this example, skipping the validation of the type of the default value until it is actually used is certainly the most sensible way to do it : most of the cases will save a few checks. And we want that performance in production. ## Static analysis has time, PHP has data Static analysis easily review the above code, and report the error before execution. The secret ? Static analysis has a lot more time to build a comprehensive view over the code. It also has time to check multiple possibilities, even when they are rare or borderline impossible. On the other hand, PHP has the actual data to process, and it doesn't need to review all the execution branches : just the one needed for the current data. Nine time out of ten, there are no problem. And the tenth time, there is a problem. Happy auditing!   --- # 6 unicity traps that sideline PHP code Source: https://www.exakat.io/6-unicity-traps-that-sideline-php-code/ # 6 unicity traps that sideline PHP code Sometimes, code is being ignored by PHP : code is painstakingly written but never gets executed. How is that even possible? May be, because it was not so painstakingly coded. And there are no less than 6 unicity traps that sideline PHP code. Indeed, there are some PHP coding structures which natively and silently overwrites or ignore code. This leads to dead code. It stays in the code base, and never gets removed. So, to help trim source code of those dead branches, here are no less than six places to check, and some static analysis to help spot them. ## Keys in arrays The keys of an array are unique; the values are not. Keys cannot be repeated, as they identify one entry in the array. One classic mistake is the repeat of a key. It may also be like here, when there is some similarity between the display of the keys. Also, be aware that constants hide those repeated values : it is the value of the constant that is used for the key, not its name. Hence, MY_CONST is actually a double of 'd'. Moreover, indices in arrays are typed `strings` or `integers`. With other data types, some type juggling happen, which lead to extra collisions. Finally, strings that contain integers will be turned into integers, leading to some really special cases : In the case of arrays, the last value is the one that stays : in reality, the last written value overwrites the previous ones. Exakat's rule : [Multiple Index Definition](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#multiple-index-definition) ## Cases in switch() or match() Very similar to the array problem, switch() and match() case's happily accept redundant clauses, and only use the first one. It is the first found, so this is the one used. Interestingly, the `default` case is immune to this behavior, and may very well be placed anywhere in the switch. It is also the only clause that will be detected as a double by PHP linter. Just like for arrays, constants may break the readability of the clauses's conditions. Exakat's rule : [Multiples Identical Case](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#multiples-identical-case) ## If / elseif / then If / elseif / then structures do not lend itself to much to repetition, until there are a lot of them. Then, the list of conditions may stretch past the bottom of the screen, and become invisible. All, in all, it is a problem very similar to the one with switch(). As for switch(), the first valid condition is used, and all the next ones are omitted. Exakat's rule : [Multiples Identical Case](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#multiples-identical-case) ## implements too much the same interface `implements` is an option for classes, to indicate which interfaces are implemented. There is no need to repeat it, and PHP checks that the same interface is not implemented twice at execution time. It reports a Fatal error when it finds one. Though, there is a dead angle here : the same interface may be implemented also in children classes, even if the parent class has already done the job. This one is silently ignored by PHP. This is merely a repeat, and since all the work is done in the parent class, it is already structurally sound. The side effects may be very limited, besides writing clean code. Exakat's rule : [Already Parents Interface](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#already-parents-interface) ## use too much the same trait `use` includes a trait in a PHP's class or another trait. Contrary to interfaces, PHP doesn't check if the trait is already used, even when there are multiple calls to it. The first one is added, and all the remaining ones are ignored. The same happens with children classes : when, again, the same trait is included, it is just ignored. The discrepancy of behavior between interfaces and traits is actually somewhat intriguing. Exakat's rule : [Already Parents Interface](hhttps://exakat.readthedocs.io/en/latest/Reference/Rules.html#already-parents-trait) ## catch clauses Catch clauses are added after a try block to intercept exceptions, depending on their class type. This structure also accept multiple times the same clause, and only process the first one, in coding order. Like for switch, some aliasing may be messing with the readability of the clauses : here, C is an alias of A, which leads to a double clause. PHP will not check double clauses : it will simply process them in order until one can process it. In fact, PHP also doesn't check if the caught exception exists or not. PHP only matches the current exception with the requested one. This is dead code, all by itself. This structure has a second trap : inheritance. The clause may be using a parent of the class. That way, the name of the exception is not used, but its parent is. This may also lead to a dead code among the catch clauses. Exakat's rule : [Multiple Exceptions Catch()](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#multiple-exceptions-catch) # Conclusion Checking for double use of an element in the syntax is a classic trap in coding. We listed them for PHP here, though every other language has the same problem. One of the thorny issue is to compare values that may actually be very different. In the case of catch clauses, the same exception may have a fully qualified name, a relative name, an alias, a parent or even a class_alias() class. The last ones are only available at execution time, hence, the late validation. Most of the time, those structures are statically coded, and they are caught by static analysis tools. All the situations above have a specific rule in Exakat's engine to spot them. And indeed, the impact of dead code is a larger burden of maintenance, and should only be dealt with at code review. --- # One less hidden bug with Enum Source: https://www.exakat.io/one-less-hidden-bug-with-enum/ # One less hidden bug with Enum PHP 8.1 provides now [enumerations](https://www.php.net/manual/en/language.types.enumerations.php), a.k.a. enums, as a native PHP construct. They provide a custom type with a limited set of values. Which might solve one less hidden bug with Enum. Enumerations have been around in PHP in various forms, for a long time : check [bensampo/laravel-enum](https://packagist.org/packages/bensampo/laravel-enum), [spatie/enum](https://packagist.org/packages/spatie/enum), [marc-mabe/php-enum](https://packagist.org/packages/marc-mabe/php-enum), [eloquent/enumeration](https://packagist.org/packages/eloquent/enumeration) or even any framework ([CakePHP-enums](https://github.com/CakeDC/Enum)). Before that, or also, when there is no need for complete enumeration system, sets of constants were used to implements enumeration. You can still find those in PHP and in custom code. Type may be `SQLITE3_INTEGER`, `SQLITE3_FLOAT`, `SQLITE3_TEXT`, `SQLITE3_BLOB` or `SQLITE3_NULL`. Those are constants and actually integers. In custom code, constant-based enumerations may look like this : # PHP 8.1 enum cases are object One detail that caught my attention when learning about the PHP 8.1 enumerations, was that the cases from an enumeration are actually objects by themselves. "[each case is backed by a singleton object of that name.](https://www.php.net/manual/en/language.enumerations.basics.php)" says the mighty PHP manual. This is an important detail, since it is a break from the vast majority of enumeration implementations so far : enum cases are usually scalars. That is, they may be compared together for identity or difference, but also with < or > or directly displayed. When referring to the custom code example, both cases were booleans. Later, they might be upgraded to int or strings when new cases arise. In any case (sic), it leads to possible confusion at usage time. # Scalar enumeration cases may be confused Let's expand the previous example with a second enumeration. The underlying feature is not important. So, now, the function has two different options, both of them are build on top booleans. This is easily understandable, until the user of the function has to remember the order of the arguments. Is it translation first, or case sensitivity first? For the author of the function, it is probably obvious. For anyone who has to learn how to use the function, this is an extra piece of doc to remember or to bookmark. And don't get me started about using the actual boolean values, as it actually remove implementation details from the code. Please, don't do that. # Datatypes for enumerations With PHP 8.1, these options are easy to check with datatypes. In case of option switcharoo, `Fatal error: Uncaught TypeError: processString(): Argument #2 ($case) must be of type Casing, Translation given,`is reported. ## Old scalar enums Typehints are not helpful when relying on scalar types. Here, both of them are boolean, so they are simply validated and accepted. Also, in advanced cases of confusion, the constants might actually be inverted, yet still be a valid business case. Since they represents the same scalar values, PHP will now process the calls correctly. Unit tests will also be happy, since the behavior is correct. And, at audit time, it will require a lot of attention to realize that the string is not process with TRANSLATED case sensitivity. Human brain is easily spooked by switched elements. You can try your luck on those actual bugs : ## Enumeration cases as integers One alternative to combat this problem is to avoid reusing the scalar values for different constants. For example, the constants were re-valued to all distinct integers. They can't be confused anymore. This works well as long as the options are directly compared to their values. In fact, any usage of `default` in a switch or `else` in a if/elseif/else structure will process them silently. It is also difficult to ensure distinct values for constants, across a large and dynamic application. You may run into questions like 'Why do my constants has to value 12523 and 15277? true and false are a lot better!' ## Scalar constant enumerations are hard to spot With PHP 8.1 enumerations, the list of available options is well defined, and easy to find in the code. They lie in the enumeration definition. With PHP constants, it is difficult to distinguish between a set, and any other constants. For example, PHP requires all constants to be prefixed, so the internal list look like this : PCRE_ prefix is easy to spot, though PREG_ prefix actually holds 4 sets of enumerations. # Less bugs with PHP enumerations PHP enumerations definitely help combat this confusing problem. Admittedly, it is a rather rare bug, though it is very painful to find and fix : the old fight between critical and rare issues. In reality, solving this kind of issues is best with a fresh pair of eyes, like a new team member. She might raise the important question : 'mm, it seems the arguments are out of order. Exakat offers a Static analysis rule called [Use Constant As Arguments](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#use-constant-as-arguments), which reports those problems for PHP native functions. For frameworks or specific libraries, documentation is needed to detail which constants are available for the arguments. That is, until PHP enumerations are adopted. --- # Interfaces are not class templates Source: https://www.exakat.io/interfaces-are-not-class-templates/ # Interfaces are not class templates [Interfaces](https://www.php.net/manual/en/language.oop5.interfaces.php) are an important part of OOP, in PHP as in other OOP languages. According to the manual, "Object interfaces allow you to create code which specifies which methods a class must implement, without having to define how these methods are implemented." And interfaces are not class templates. Besides interfaces, PHP also supports `abstract` classes, which may define both `abstract` methods and `complete` or `concrete` methods. The `abstract` class can't be instantiated as long as there are abstract methods. That way, they provide a template for any child class, ensuring that those methods are available. They may also include properties definitions and full methods, unlike interfaces. They need to be extended, not implemented, which is a different modus operandi than interfaces. Yet, abstract classes and interfaces are pretty much interchangeable. ## Just fill the holes The templating nature of interfaces is checked by PHP at linting time. This is the most obvious to any PHP coder, since PHP will refuse to lint or execute when the interface-template is not correctly filled. Indeed, the following code yield an error : Error : `PHP Fatal error: Class x contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (i::foo)` Once the method has been created, aka it has a body, or when it is also marked as `abstract`, aka, it has no body, then PHP will move on with execution. ## interfaces usage At that point, we can notice two interesting side effects of the definitions : - An interface defines methods, from none to a lot - An interface has little scope after initial linting Let's review these points in order. ## Constant only interfaces An interface made only of constants is an interface without methods. To avoid defining global constants, it happens that constant-only interfaces are created. That makes theses constant easy to import in any class, or also, act as global constants, since the constants are public. This is a niche use of non-method interfaces. ## No method interfaces With or without constants, interfaces may offer zero methods, while being a valid interface. This way, the interface acts merely as a class tag, or an alias or even some kind of weak attribute. When several classes are tagged with `i`, it is possible to filter on that arbitrary group of classes with the `i` interface. In the example above, `x`, `y` and `z` have been grouped, for some reason, and `a`has been singled out. Class hierarchy would not allow that grouping : `a` should not be in that group, yet it extends the `root` class. Obviously, it may be hard to use completely independant classes together : such classes require different processing methods. Yet, when those classes belong to a common hierarchy, or implements yet another interface, using an empty interface acts as a refinement. Having the special interface `extends` the refined one may be more secure, until the code can use the intersectional types of PHP 8.1. ## No typehint interfaces Now, back to the second point : when an interface has methods, then it acts as a template for the implementing class. PHP does check the presence of the actual methods, then proceeds to execution. And, one can only notice that once the methods are implemented, the interface is itself is actually useless. Here, `x` implements `i`, and so does `y`. But `y` does not wear the `implements` keyword. ## implements methods, skips interface In fact, it is possible that some classes satisfy the constraints of an arbitrary interface, but do not use the `implements` keyword. We should check existing classes for possible missed `implementations`. In the example above, class `y` is missing the `implements i` keyword. This is the actual goal of the Exakat's [`Forgotten Interfaces`](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#forgotten-interface) rule, which spots classes that have the necessary methods to implement available interfaces, but do not use the `implements`keyword. The ultimate decision of using an interface or not is actually a conception level one. The notion of grouping or tagging classes with interfaces, as we saw above, may be arbitrary. And also, it might be a simple forgotten keyword. ## Non-useless interfaces So, if the interface is not a template, how does it get useful beyond the linting phase? Just as we saw for empty interfaces : by using them with typehints and instanceof. Both of those features check objects for a class or an interface. As soon as the interface is used in a typehint or instanceof, it cannot be removed anymore without impacting the code. The interface is now needed at execution time, to branch execution or filter data. Such interfaces are not useless anymore. ## Interfaces are not class templates When using interfaces, remember that they are mere templates until they are actually used in a typehint. Otherwise, they may be dropped without impacting the code. They may also be end up being missing in classes that implements their methods, but not the interface. In fact, usage and definitions may also help programmers define new interfaces : when two (or more) classes define two (or more) methods with the same signature, they could be considered eligible for wearing a common interface. This will help typehinting strategy. In the following example, several interfaces may be extracted from this list of classes : one with `function a();` only for 3 classes, one with `function a(); function b();` for 2 classes, one with `function a(); function c();` for 2 classes, The combinaisons will soon make the challenge of naming all those interfaces very hard. This is a typical example of organically growing code, where new interfaces are infered from the code, rather than from the conception. Yet, it show the evolution of the code and that may be crucial to any auditor. --- # Literal candidates for constants Source: https://www.exakat.io/literal-candidates-for-constants/ # Literal candidates for constants Literals are hard coded values in the source. Once they are written, they can't be changed without altering the source code. This is always fine, initially, while building the first implementations. Yet, later, it may arise that such literal have to be managed from different points of the code. This is where constants come into play, and where one need to find literal candidates for constants. The hard part of defining constants is deciding when a literal is a good candidate. Depending on the maturity of the code and its connexion with other remote parts of the application, a constant definition is a delicated decision : it may never happen. And, it is just too early to create a constant. Let's see how we can detect some candidate with two strategies : frequency and patterns. ## Literal frequency The first approach to convert a literal to a constant is to a observe its duplications. Once a literal is used multiple times throughout the code, there is a need for it to be disambiguated. Here are three octal integers, used in Exakat's code : - `0755` : 23 times - `0700` : 3 times - `0777` : 1 time Octal integers have usually only one usage in PHP : setting the privileges when creating a new folder. Here, the `0755` is the classic default value. Its usage is easy to understand, and its frequence makes it a good candidate for refactoring into a global constant. `0777` is used once only, so it might be a lone code. Indeed, this is part of an analysis called [Keep Files Access Restricted](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#keep-files-access-restricted), which tracks folders created with loose privileges. Then, this literal is different, and it should not be turned into a constant, just like `0755`. ## Constant disambiguation Finally, the frequency approach applies to `0700`. It looks like a stricter version of the `0755`, even though the literal is distinct from `0700`. Manual review is needed here, and it could lead to the merging with the newly defined constant for `0755`. That's how consistency across the code is improved. This is a process called disambiguation, where the same intent was applied with different literals, and consistency was lost in the process. Here, we have an example of literals which are different, yet could be merged under the same constant value. In fact, the same disambiguation could also happen with high frequency literals. Take, `true`, `0`, `1`, `10` or `256`, for example. Those are used in various occasions, and they might be distinct applications. For example, Exakat uses the string `'none'` to configure some properties : for class constant visibility, method visibility, block of code presence, or baseline existence. While it is semantically valid to use this word in each of those situation, it is recommended to distinguish the situations with a different constant name. ## Code pattern usage The disambiguation process leaves the programmer in a grey zone : when the frequency of a literal is low, it is not complex to dectect its meaning, and it is easy to refactor the literal into a constant. When the literal is used in many different situations, sorting the various cases is difficult. One strategy to sort this is to use pattern discovery : detect a recurring pattern in the way some literals are used, and then, group all those usage together. ## literals as messages Literals may be used to carry a simple message or state. The state is set in one part of the code, then it is later retrieved to identify it. This is the case in the following code : The display of the message is configured with the assignation of 'yes', then it is later checked again with ==. Also, note that this could have been a boolean, or even, a boolean with a constant named 'YES'. The string helps the human coder understands the semantics of the literal. A constant would also do that work : host a literal, and give a readable description of it in the code. ## Code pattern At that point, a pattern is easy to identify. The literal value has to be set, and the same literal has to be part of a comparison. Following the container for the literal is possible in the above illustration, since the assignation and the comparisons are close. Yet, those messages are often set up to enable communication between remote or unrelated part of the code. So, the storage of those messages could be ignored, yet provide interesting results. This leads to [recognizable patterns](https://www.exakat.io/reports/yoastseo/data/could_be_a_constant.html) like the followings (applied on [yoastseo wordpress plugin](https://yoast.com/wordpress/plugins/seo/) : - 1 `$clean = '1'` - `$site->$state_slug === '1'` - `$type === '1'` - full `$image['size'] = 'full'` - `$image_size = 'full'` - `$size === 'full'` - on `$val = 'on'` - `$_SERVER['HTTPS'] === 'on'` - `$meta_data['wpseo_noindex_author'] === 'on'` - `$value === 'on'` - `wpseo_manage_options` `$submenu_pages[$index][3] = 'wpseo_manage_options'` - `$capability === 'wpseo_manage_options'` ## Interesting takes and limitations The shorter (or smaller) the literal, the better. When the size goes up, it becomes harder to write in the code, and error prone. This leads to the adoption of a constant, to minimize those errors. Some of the usage of the literals are completely distinct and not related. The semantics of the containers may be helpful to spot false positives : `$_SERVER['HTTPS']` is read-only, and `$clean`, `$type`, `$site->$state_slug` look very unrelated Some of the literals are later combined with other values, through concatenation or append to an array. For example, a file extension `.php` or a network protocol `https`. Those should be skipped, as we are looking for a token, aka a piece of string that is used as a whole, not a part of anything else. (this is not illustrated here). ## Conclusion To avoid the effect of the magic number, setting up constants allows the code to become a lot more readable, yet surprisingly efficient to run. Constants allows to write a meaningful code, and apply a machine useful literal. We also shown how code patterns emerge in the code, and how static analysis may take advantages of them. The process is simple : identify some common behavior, turn it into an analysis rule, then run it on several pieces of code. And then, the most important : review the results : the one that were expected, and the other cases. Other cases are critical, as they help us understand other patterns. One way to go further with this pattern is to identify enumerations. Even before PHP 8.1, enumerations group several constants together as a consistent group. Values are tested and mutually exclusive within the group. This often means using `switch`or `match`, or even `in_array()`, with a special array that groups all those enumeration cases. Could we suggest enumerations, while spotting those usage? What would be the false positives? Code pattern are such a rabbit hole. --- # Exakat 2.4.4 Review Source: https://www.exakat.io/exakat-2-4-4-review/ # Exakat 2.4.4 Review Exakat 2.4.4 brings a host of improvements, at the engine level, and with new analysis. ## Preparation for Gremlin 3.6.0 With this version, Exakat has started the move to Gremlin server 3.6.0. This is an important move, as Gremlin tighten the bolts of the language. In the long run, it will help choose among the numerous [Gremlin engines available](https://project-awesome.org/mohataher/awesome-tinkerpop). ## New analysis - [Retyped References](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#retyped-reference) A parameter with a reference may be typed differently, at the end of a method call. - : Report type error for default values - ext/ice : [Ice Framework](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#ice-framework) for the ice framework - ext/taint : a [tainting tracking system](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#extensions-exttaint), by [Laruence](https://twitter.com/laruence) - Some old extensions were removed, as they are very old and unsupported. ## Migration to PDFF Some rules are refactored with the PDFF files. PDFF stands for [PHP Document File Format](https://github.com/exakat/pdff), and describes in great details an external component, a framework or a PHP extension. With it, it is possible to review code without downloading that dependency. For example, the [Static methods called from objects](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#static-methods-called-from-object) now takes into account any configured external components. # Happy PHP Code Reviews All the 1500 analyzers are presented in the manual, including the noble : [Large try blocks](https://exakat.readthedocs.io/en/latest/Rules.html#large-try-block)): Avoid using too large a try block. Keep it small, and focused on the actual call that may raise the exception. You can check all of the Exakat reports at the gallery: [exakat gallery](https://www.exakat.io/exakat-gallery). To use Exakat, you can install it as a phar archive, install it with [Docker](https://hub.docker.com/r/exakat/exakat/), upgrade it with 'exakat.phar upgrade -u' and like us on [github](https://github.com/exakat/exakat-ce). It is also available as a [Saas Platform](https://app.exakat.io/). --- # Some Aging Parameters Source: https://www.exakat.io/some-aging-parameters/ # Some Aging Parameters Parameters are part of the signature of methods and functions. They are one of the ways to inject data into a piece of code, for it to be processed. PHP has a lot of options and they have been available since ever. They are easy to find in recent and legacy code. Like everyday's code, parameters tend to grow old and require maintenance. They come and go, and move around and get renamed. Each transition is a new challenge to refactoring, which mean there will be traces of evolution right in the code. In this article, we'll code some of the remainers of such evolutions. Let's review some sicknesses of parameters : - The removed parameter - The unused parameter - The cancelled parameter - The hiding parameter ## Parameters and arguments A side note, about the difference between parameters and arguments. Parameters are in the definition and the body of a method or a function, while arguments are inside a method or function call. They are two distinct sides of the same data, and they share names and options (typehints, reference, variadic,...) but they are distinct. [![](https://www.exakat.io/wp-content/uploads/2022/07/parameter-arguments-1024x390.png)](https://www.exakat.io/wp-content/uploads/2022/07/parameter-arguments.png) ## The removed parameter The removed parameter is a parameter that was dropped, but the corresponding argument is still in use. This means that the third argument is passed, but not processed. Note that the extra argument might be processed : this happens when the last argument is variadic, or if the func*get*args() function, and similar, is used. PHP doesn't report any error when the call doesn't match the signature : the arguments are processed as they are provided, and any extra argument is omitted. That way, the only visible artefact is the extra parameter litering the code. It is rather simple to fix : remove the argument in the code. And, by extension, all the code that was needed to create the argument. The example above show the last argument being fetched from the `goo` function : in fact, the whole call is now useless. **[**[**Wrong Number Of Arguments]**](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#wrong-number-of-arguments) ## The unused parameter A close cousin of the removed parameter is the unused one. This time, the argument is never explicitely used in a call. Then, the method uses the default value. When the method has no default value, PHP raises an error : this is visible and easy to spot. By removing the default value, the parameter forces any calls to provide an explicit value. On the other hand, when a default value is provided, it might address the most general case, yet leave an option for more specific and rare situations. It is one case of Reserved For Future Use (RFU). Such parameters are nice in the early stages of development. After a while, it is worth reviewing all the calls to the method, to check if the parameter is actually used or not. And when it is not, it might be removed. **[[Unused parameter](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#unused-parameter)]** ## The cancelled parameter The cancelled parameter appears when a parameter has to be removed, but is more difficult to update without impacting significantly the calling code. In the following code, the first parameter has to be set in stone, but there are already a lot of calls to it, making it difficult to update. Here, the parameter has been kept in the signature, to keep the current code working. Yet, the incoming value is totally ignored, and forced to a defined one. This quick-fix makes documentation quite difficult : a useless parameter means that the call must use a filler, and then, typehinting might complain about it, even though the argument is later ignore : for example, using a string in the code above blocks the call. It appears in situations where the calling code cannot be easily modifiable : for example, with legacy applications, or external user. Removing the parameter means documenting it, and explaining to all how it works. Other options include relying on named parameters to skip any unwanted parameters, or creating a relay method, which has the old signature and drop the unwanted parameter to call the new signature. **[[Cancelled Parameter]](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#cancelled-parameter)** ## The hiding parameter Finally, there is the hiding parameter. This happens when the parameter is used, but under another name. Basically, the argument has a name, and the parameter has another one. The example is simplistic, so, let's devise two actual ground stories of hiding parameters. The first one is the copy-paste of code from known sources. When the copied code is too long, it might be too much to rename one of the variables, and make it a parameter. Then, the parameter is named, poured into the variable, and used as is. Sadly, this happens more than expected. The second situation is the documentation of the method. Until PHP 8.0, parameters names didn't matter, except for documentation. The name of the parameter is significant, so it is nice to give is a good name. Yet, it was a totally internal matter so far. With PHP 8.0, named parameters are also used outside the method, and become part of the API. Using non-descript names is not possible anymore, as those names have a value in the calling code. In both cases, refactoring the code with a meaningful name is much better. It doesn't take much time, and IDE do help with such rewrite. **[**[**Parameter Hiding]**](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#parameter-hiding) ## Conclusion Hopefully, running into those patterns is not common. The removed parameter is easy to spot, as long as one has the knowledge about it; the unused parameter is very difficult to track, as it implies checking every usage; the cancelled parameter requires a trained eye and the hidden parameter is usually a surprise, more than anything else. All those situations may endure for a long time, as they do not report any problem. This is an exact application of the saying : if it works, don't fix it. The impact on the code might appear elsewhere, in the skewed documentation, the surprising code or a vague feeling of unexplicable things. The best solution is to refactor them, into a single functioning or removed argument. To detect it, static analysis does help by searching those rare but wasteful code patterns. The last decision is then: when to refactor them. --- # Exakat 2.4.6 Review Source: https://www.exakat.io/exakat-2-4-6-review/ # Exakat 2.4.6 Review Exakat 2.4.6 finishes the movement to Gremlin 3.6. It now prepares PHP 8.2, with the upcoming feature freeze by the end of July. ## Preparation for Gremlin 3.6.0 With this version, Exakat has finished the move to Gremlin server 3.6.0. All drivers are now ready, and many rules have been ported to the new syntax. Gremlin is now supported by the Amazon Neptun servers. It might be a future architecture evolution. If you have ever checked out a Graph Database, [Gremlin Server](https://tinkerpop.apache.org/) rocks! ## New analysis [New functions in PHP 8.2 ](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#new-functions-in-php-8-2)the list of new functions that are coming up in PHP 8.2. They might have a naming conflict with some custom functions. ## More typehints in the Exakat Engine Several classes where upgraded with type for properties and more methods. This usually leads to some refactoring, for various practices reasons : those values may be filtered before being used, or have a temporary stage of null. Also, the default migration is made toward scalars, and they will later be upgraded to classes. Until then, they already caught some errors of type, which fixed some cases of missing dictionary usage. Scalar type do bring some goodness with them. # Happy PHP Code Reviews All the 1500 analyzers are presented in the manual, including the surprising : [No Return Used](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#no-return-used)): The return value of the following methods are never used. You can check all of the Exakat reports at the gallery: [exakat gallery](https://www.exakat.io/exakat-gallery). To use Exakat, you can install it as a phar archive, install it with [Docker](https://hub.docker.com/r/exakat/exakat/), upgrade it with 'exakat.phar upgrade -u' and like us on [github](https://github.com/exakat/exakat-ce). It is also available as a [Saas Platform](https://app.exakat.io/). --- # How to make an elephpant plush toy Source: https://www.exakat.io/how-to-make-an-elephpant/ # How to make an elePHPant generation # Table of content - Why doing an elePHPant generation - Preparing for an elePHPant generation - Folklore of the elePHPant generation - Miscellaneous info # Content ## Why doing an elePHPant generation? Here are some questions and pre requisite you need to address before starting an elephpant generation. No involvment yet, as this is the next section, and a lot of fun, imagining what it could be. ### What is an elephpant generation An elephpant generation is a batch of elePHPant, the PHP plush toy. The batch has to be 300 or more to be considered a generation, and remembered as this. That way, it means that the elephpant is accessible to the larger PHP community. A generation of elephpant has a PHPather and/or a Phpother. It also has a prototype (see later), which will be known as the first elephpant of the generation. The first elephpant might have slightly distinctive features, compared to the rest of the generation : it is still part of it. ### Values of the elephpant The value of the elephpant is the reason that motivate it to spawn a generation. Let's be frank : having a plush toy, however cute it is, is not life achievement. And, indeed, elephpants have had various purposes. Here are a few of them : - Proof of love for the PHP language and its community - To celebrate the last PHP version - To make new friends Making exchanges with other like-minded elePHPant companies - To raffle at PHP meetups and conferences - Raise money for a specific goal To bring more diverse crowds to conferences - To help promote women in the community - To rent buses and drive attendees to a common event - To finance a local user group - To promote a piece of software, an event, a company - To reward to those who achieved their goal diploma, tests, delivery, etc. - HR management To hire new PHP developers - To reward the current employees - To gift previous employees - To send a token of gratitude to Open Source developers - To build the culture of the Tech Department Name the elephpant - To run marketing campaigns Send to customers or prospects - To ask for pictures of the elephpant at the customer's place - To travel with the elephpant (Amélie Poulain effect, when the garden dwarf travels the world) - Attract attention at exhibitions with the big elephpants - Crowd-source the elephpant name in the Tech department - Hold challenges with the elephpant Make the largest elephpant pyramid - Write poems for the elephpant - Hide elephpants in the room and make an easter egg hunt - To build a large collection of elephpant by exchanging them - [your story] ... Take into account that elephpants are easily recognized as the PHP elephpant. It is best to have a significant relationship with the technology. ### Art of the elePHPant #### Colors, nails and the logo side The most classic elephpant is the official blue elephpant, with the PHP logo on the left side, so as to ressemble as much as possible as the PHP logo and the original 2D elephpant from Vincent Pontier. The simplest form of customization is to choose a different color for the elephpant fur, and its toe nails. With a little help from a local designer, it is possible to take a picture of an elephpant, check various colors and choose the one that works the best with design specs. As of today, all the colors of the rainbow have been already used : red, orange, yellow, white, black, grey, pink, blue, green, you name it. White and blue are the most common, and green is the least common. Choose the one you like. Gradient colors and patterns are very difficult to make into an elephpant. The elephpant fur has to be cut in specific smaller parts, before stitching them together. It is not possible to plan for them to be a in a specific place or orientation. The only elephpant with gradient, EnPHPys, was painted after stitching. It is possible to use several colors on the elephpant. The most clever way is to use the stitchings lines and have each 'part' of the elephpant in a distinct colors. This was done with the red and white elephpant, the blue and red, or a five colored elephpant. The right side of the elephpant is traditionaly free, and used to add a logo. The logo is stitched, and made of thread: this means that the logo should be simple, and the number of colors should be kept as low as possible. One or two colors are nice, more may raise the production price of the elephpant. The exact color, the size of the fur is usually left to the factory to choose. They might be slightly different from the requested colors, so check those at prototype time. The material of the elephpant is traditionnaly plush. After all, it is a plush toy. You may consider other material: some elephpant were done in jeans or in a golden fabric, no plush. #### Accessories For the most adventurous of us, accessories and advanced features are possible. Simply keep in mind that they will most probably raise the price and the minimum quantity of elephpant to produce. Yet, there are some room for special features. Here are some ideas and previous projects : - Pointy ears, for the Spoky elephpant - Tusk and long fur for Molly, the mammoth - Glasses for Zend elephpant - Hat for the PHP Yorkshire elephpant - Long hair on the head for the PHP Storm elephpant - Two-half colors elephpant with red and white, or red and blue. - Elephpant cowboy hat - Harry Potter hat elephpant #### Elephpant size Elephpants come in two sizes: the normal one, and the big ones. Normal elephpants are the most common and the most sturdy. They are about 20cm long, 10cm wide and 15 cm high. This is the default size when mentionning elephpants. The big elephpants are much bigger. They are meant for display, in the office or in an exhibition. With their size, they are a bit more fragile, and require more care. #### Artistic review Elephpants have to be reviewed by Vincent Pontier. This is a very benevolent review, aiming at keeping the family of elephpants sharing the same DNA, and some family traits. That stage is quite simple : send a mail to Vincent, with a quick description of your intended design. When your elephpant is in the right spirit, his review keept it all. ## Preparing for an elePHPant generation This section deals with the realities of making an elephpant generation. Once you have secured the questions of the previous chapter, it is time to make it a reality. This comes with hard questions, like money, custom certification or where the hell will I store so many plush toys? Make sure to read the previous chapter first, as this section is the non-fun part, with all the potential deal breakers. ### Who to ask about an elephpants generation It is a good idea to get in touch with some people that will help you shape up the generation. They can share experience, recommendations and answer specific wishes. - Vincent Pontier (@elroubio) - Damien Seguy, (@faguo, @dseguy@phpc.social) - Manuel Lemos, (@mlemos) ### Timeline Creating a generation from scratch takes about 6 months (six). This is quite a long process, and it will probably be faster than this forecast. Yet, it is recommended to start planning with such a timeline. Here are the various steps that you may encounter along the way. Good news, you've already done the first one! - Prepare the art of the elephpant - Check your target audience and size the generation - Secure the budget - Make a prototype - Validate prototype and start production - Tease the community with the prototype Name the elephpant - Finish paiment for production - Wait for delivery - Celebrate the delivery of elephpants - Ship elephpants to their new families ### Size of the generation The size of a generation is important: at the moment (2023), 300 elephpants are the lowest minimum to reach a production level. Upwards, there are various threshold where the unit price drops sharply. It is often interesting to check if the next level is not actually cheaper than doing a smaller generation. The next important threshold is 900, where that particular generation is allowed to make the big elephpants. Also, unit price gets lower at that level, and is probably the most interesting of all prices. Larger generations of elephpants are possible, yet rare. The record is currently of 6000 in one batch. The size of the generation is important for the budgeting part (see next section), but also for the storage of the elephpants, once they are delivered. Each group of 50 elephpants makes a box, and 16 boxes makes a cubic meter : 1m x 1m x 1m. It looks small, but this means quite a lot of place. Plan accordingly, before a long truck stops at your office. ### Budget Budget is quite straightforward when the generation has been sized. Simple, yet not easy. The figure that are offered here are based on estimates, and in July 2023. Use them when working on the idea, but always check them with the actual production site, as they are subjects to fluctuation, including inflation, custom fees, labor fees, international shipping and insurance, exchange rate, etc. Elephpants cost estimation is about 15 euros/USD each, all included. This estimate covers production, royalty fees, shipping by boat, toy certication (Europe CE); it is for normal sized elephpant (aka, not the big ones). It is a good figure for budgeting. Small elephpants batches starts at 300 (three hundreds), and the largest batch ever was 6000 (six thousands). Unit price decreases with the size of the order, and the budget always grows : in case of doubt, stick to a smaller batch. Large elephpants batches need a 900 elephpant order, and may be made by unit. Each is about 100 euros/USD each. ### Shipping Shipping is usually offered by boat or by air. Air shipping is a lot faster, but also a lot more expensive. Given the timeline, it is usually worth waiting a few weeks more and get the elephpants in a cheaper and greener way. ### Certifications Elephpants are produced to meet the certifications of any world market, in particular the European market (CE marking) and the north american markets. The certification and custom process is part of the shipping, so they are delivered with the valid custom review. Once inside a country, the elephpants may be shipped again anywhere within that country, or any other country with which there is a trade agreement : once the elephpant have been cleared, they are cleared for all commercial partners. This applies with the EC or Alena. If part of the elephpant generation has to be dispatched to a remote destination, the same certification process may be required. This is in particular true for large orders : customs will want to see the certification profile again. As for that, it is possible to request for a separate certification at the factory level. This will come as an extra set of document, which, in turn, may be transmitted to any authority. This will save the process of certifying again the elephpant, after the delivery. ### Shipping the elephpants Shipping is the most scary part of the elephpant generation. The whole cost of production has to be paid before the elephpants are put on a boat. After that, the elephpant do travel the world, pass customs and are delivered to your address. There is little information about them along the way, so it is a bit of a tunnel. Do allow for 3 weeks at bare minimum: that would be shipping by boat from China. Many events may occur during that time, and the most common are delays en route, port entrance file, a landing port a bit further than expected, customs checks for the elephpants or anything else that is on the same boat. Custom may also take more time, as the elephpants are considered as children products. The best is to prepare the reception (see the folklore section for the unloading), storage, re-shipping and online presence. That should keep you busy enough. In any case, keep in touch with the producing company, as they have better tracking information. ## Folklore of the elePHPant generation A large part of making an elephpant generation lies in the folklore around it. Planning it, making it, receiving them and shipping them is a long process, with its own specific silly traditions. Here are some of them. ### Delivery In English, the same word is used for both assisting baby birth and reception of goods : [delivery](https://www.merriam-webster.com/dictionary/delivering). So, an elephpant generation has a date delivery to the happy Phpathers and PHPothers : by extension, this is the date the elePHPants are born. ### PHParents : PHPathers and PHPothers Just like elePHPant is build as a porte-manteau from elephant and PHP, a wide range of words are porte-manteau-ed the same way in the PHP world : P's and F's are replaced by PHP, leading to a new, strange yet vaguely familiar word. This applies to the PHPamily: a delivery of elephpants is an addition to the great elePHPant PHPamily. ### Triaieul, the first elephpant The first elephpant goes by the name of 'Trisaïeul', a French word meaning great-great-grand-parent. Although, this first elephpant also goes as the 'grand-father of all elephpants'. Also noteworthy : there are currently (2023) over 70 elephpants generations, which would make that name a long long repetition of 'great'. The first elephpant is still alive, in the custody of Damien Seguy. It is a great honor for young elephpants to meet him, and pay respects. Find him at conferences, meetups, exhibition or travelling the world. He is always happy to make pictures. ### The elePHPant mother Elephpants are known to have one great-grand-father, aka Trisaïeul, the first elephpant ever. NO ONE asks who was the partner of that first elephpant, carrying about 6000 cubs in one pregnancy: just NO ONE. ### Dedicated elephpants Each generation may dedicate elephpants to some outstanding members of the community. For example, the first generation of elephpant was offered to the members of the PHP group, in 2007. Nowadays, Release Master get a PHP 8 inphpinty elephpant, as a token of gratitude. Vincent Pontier is requesting 3 elephpants for his own museum. Besides the last one, there are no special rules to distribute the elephpants : they may be shipped to anyone you like. ### Unloading the elephpants The delivery of elephpant is an important moment. There are several large and cumbersome boxes to take out of a truck, and store them in a dry and safe place. It is a tradition to bring everyone involved in a PHP generation to help unload the boxes. It comes from the first delivery of elephpants, where 54 boxes where unloaded by throwing them from one developer to the other. Even with the right rythm, it ended in a messy pile of card boards. Later, several generations where unloaded manually, with everyone remotely involved with PHP giving a hand. The legendary level was when the delivery truck parked next to the warehouse, but 60 developers showed up to help. In the end, they lined up across the parking, and the elephpants boxes went the really long way into the warehouse. Also, it rained, that day. Of course, there may also be a warehouse dock and a forklift available, and it will do the job fast and clean. It's just not fun, but go ahead. ### Washing the elephpants It is possible to wash the elephpants, using every day appliance. Make sure to use a very gentle washing program, and low temperature. Also, after drying, you may have to work the plush inside the elephpant to spread it around its body : no need to open the sewings, pressing and massaging the content from the outside is sufficient. Long, but sufficient. ### Customizing further the elephpants Once the elephpant have been delivered, they might be further processed. Being material and thread, it is possible for a skilled couturier or seamstress to add extra features, or an accessory. By experience, it becomes quickly an expensive endeavour, as each of the elephpant has to be processed manually. Besides, even if they are industrially produced, their plush and fabrics nature makes them all distinct. Simple operations, such as adding a sticker on the elephpant, or pairing it with a hat are possible and reasonable. ### Shipping the elephpants While the big batch is delivered at one address, there is often the need for re-shipping. This means breaking the boxes, and sending smaller batches to particular addresses. This operation is simple, though it does take quite some time and energy. It is important to plan for it. Check with the post office how you can ship those elephpants. No need for a special machine, but buying stamps in bulk is useful. Also, getting those custom forms for shipping abroad is critical, as you'll have to fill them quite often, if not all the time. By the unit, elephpants are about 20cm x 10cm x 10cm. They are also malleable and compressible: a tight box doesn't scare them. Check with the post office, as they may have a standard box for that size, and if not, any office supply brand. While you're at the office supply, include some more scotch tape, and a few pens. Tags to print addresses with a printer is also a good idea. Finally, consider reshipping boxes as a whole : that will be 50 elephpants, available somewhere for local distribution with a PUG, an event or a local company branch. Batch shipping is a good idea, as it saves the cost and generate a small but significant event. ## Miscellaneous info ## elePHPants and elePHPants ElePHPants actually have two meaning : - The original drawing by Vincent Pontier, and all its derivatives - The plush toy elePHPant, based on Vincent Pontier drawing, and produced by Damien Seguy. Although both notions are close, they are two distinct art productions. ## Special cases and accomodations Of course, each elephpant generation is different, and may be prepared in a slightly different way from the others. Half production by boat, and half by plane? Biological fur or multi-colored toe-nail? Each in a box or all under a vacuum ? Local factory or remote one? Extra certifications ? While this guide get you through a lot of details, some of those have to be discussed directly with Vincent Pontier or the production facility. Some are possible, others cost more and some are not possible : if you have a special idea for your elephpant, come and discuss it, we'll do our best to have it done or find an alternative. ### Elephpant naming Once you have an elephpant, you need to give it two names : - a secular name. This is the commonly used elephpant name, and it default to the author. There are PHPcon elephpants, a.k.a. Polish elephpant, a.k.a. Janusz. The name may be anything. - a latin name. This name is for science purposes. Create that name as a tagline, with pig latin. Fun is of the essence here. ### Unique elephpants names Besides the generation name, some elephpants have achieved famous as individuals. - Trisaieul: the first elephpant ever. Famous for being the first and oldest elephpant. Recognizable with its blue PHP logo on the left. - Traversable: the travelling elephpant. Famous for going from speaker to speaker, across the world, with its passport. Recognizable with it piercing on the ear, and its accompanying passport. - Papa: the big elephpant from the first generation. Recognizable to its tired fur, and very slim figure, as it was slept on a lot. - your elephpant here... ### Elephpant smuggling Elephpant smuggling covers all the activities around providing elephpants to any region of the world. It smells like contraband, but since elephpants are legal in every countries in the known universe, it is actually a legit activity. There are stories of carrying a lot of elephpants in suitcases onboard planes and trains, or bringing rare elephpants from an event to another part of the world. For example, 400 elephpants in a plane to Barcelona, the aborted exchange of elephpants at the Thalys 3 mins stop in Brussels, or the successful elephpant delivery on a hidden Neuhaus chocolate factory. Sometimes, the fun lies in making it sounds like it is a spy novel, although, indeed, it is mundane. ### Community Postal Service The community Postal Service, also known as CPS, is a cheap but slow way to ship elephpants across the world. The trick is to find someone who can travel to a location closer to the destination. There, the elephpant shall be given to someone else, who will bring the elephpant again closer to its destination. The process is repeated as much as necessary. CPS is free, and mostly requires knowing someone who travels. The down side is that it is difficult to know when the elephpant arrives. Sometimes, it will take up to several years of transit. CPS is very fun for the patient ones: it puts people in contact, and they usually exchange information and updates during the trip. It makes the base for very complex elePHPant stories. CPS was used successfully with the following lines : - San Francisco (USA) to Seattle (USA) to Amsterdam (NL) to Brussels (BE) to Koln (DE). - Brasilia (BR) to Metz (FR) to Amsterdam (NL) - Omst (RU) to Berlin (DE) to Amsterdam (NL) ### Batching elephpant orders When buying elephpants online, check if there are already a grouped order. Sometimes, it is cheaper to buy a big box (50, one of the classic box size of production), and spread the shipping costs across several buyers. Grouped orders are usually made online, on social networks. They are also done when someone discovers the elephpant shop: it might be anyone, including you. This strategy is best when coupled with CPS (see above). The orders are collected online, and a big order is shipped to one address. Then, a mix of meetups and informal meetings is used for the last mile logistics. This is also good for the environment, as much less shipping resources are used. And it brings the community closer. ### Sourcing new elephpants when they appear Finding a source of elephpant is quite a little event. Feel free to mention it online, and offer remote collectors the possibility to access them. It is then easy to acquire a set of them, and then, spread it to whoever needs them. Use social networks for that. ### Elephpants exchanges When finding a new source of elephpant, it is wise to get several of them. You can add one to your collection, and make the others available on the [elephpant.me](https://www.elephpant.me) Web site. Elephpant.me is a market place where you can describe your herd of elePHPants, and also, initiate exchanges. Find a specie that you don't have in your collection, and look for anyone who has it in double. Then, make an exchange with one of your own. ### Elephpant world collector ranking Elephpant.me (see above) also includes a world ranking. Each collector is ranked by the number of unique elephpants they possess. One of each specy is needed to progress. The website has world and per-country ranking. ### Field Guide of elephpant The [Field guide of elephpant](https://afieldguidetoelephpants.net/) is the reference of elephpant species. It includes all the existing elephpants, with their name, pictures, latin description and date of availability. When building a new elephpant generation, it is wise to submit a PR to that site, and have your elephpant listed there. ### Elephpant collectors There is a group of tenacious elephpant collectors. These people, including the author of this page, are relentlessly looking for the missing elephpants in their collection. They will go great length to snatch the elephpant they are looking for. It is a good idea to book about 100 elephpants in any generation, and set up a simple shop online to sell them. Whatever values are driving your generation, there are collector who want to fork out a few monies and get it. It actually offsets a part of your elephapnt budget. By selling 100 of them, they will help you finance your own generation, and you'll be making a lot of people happy. --- # Adding the last types to PHP code Source: https://www.exakat.io/adding-the-last-types-to-php-code/ # Adding the last types to PHP code Adding types to PHP code is a staple of any PHP code refactoring. With a code base crawling towards 10 years and over 2000 classes, Exakat is quite a consistent piece of software, with a lot of parameters, methods and properties. Covering everything with types was long, and, at time, tortuous. I have to admit, it also feels like 100% test coverage. Somewhere, between 90 to 95% of type coverage was the sweet spot. Yet, going beyond that level required a lot more effort, and possibly less spectacular rewards. The challenges it posed were interesting, so I wanted to share them and the adopted solutions. Some of the issues revolved around adding invisible types, handling the default values, managing temporary values, deciding when to use union types, wrestling with resources and juggling with cache mechanisms. Let's go! ## At first, typing is very easy Before the last leg of the journey, there was the first leg : obviously. It was the easy part, and, in the retrospect, possibly the least useful. Typing code that already works well doesn't change anything : it keeps working well. Adding the first types consists in looking at the best maintained part of the source, and saying : yep, I know this is a string or an array, or an object of type `A`, for sure. And then, adding the type. And of course, there are very little mistakes at that stage. There are multiple ways to guess the correct type : knowing the code and its intent, incoming argument, returntype of methods, PHP native support, usage of methods and properties, comparison to literals, usage with operators, type propagation,... I have already made several conferences about automated typing, where the code can be typed with very little human intervention. Even, Exakat is capable of guessing and adding such types to any code. During this phase, the types flow naturally, and rarely lead to any surprise error. The code was under control, and adding the type doesn't really change anything. A rough estimate is that until 2/3 of typing coverage, there is no real challenge, nor, any rewards. A.k.a., so special case is discovered. In fact, I suspect that any ambiguous decision about types was inconsciously set aside for later. The obvious cases are quickly added, and any hesitation leaves the property untyped. When the coverage is still 32%, and there are still several thousands of them to add, no one notices an untyped property. ## Then, came the hard phase When the easy types started to become rarer, the harder to decide types had to be added. This stage covers situations that were not obvious. Sometimes, it took running the tests and some examples to see a fatal error emerge. Trust in the type system was now crucial : adding a type could mean an error later. ## Null is not a default value anymore This one is very easy to understand, and, for some reason, it kept coming back. It really looks like fighting an stubborn habit. Look at the code below : there is an obvious type for the argument, which later is used to call a method. Adding the type `A` is a no brainer. Now, let's see the same situation for a property, which later is used to call that same method. Adding the type `A` is also a no brainer. But there is a catch. When adding `null` as default value to a parameter, the `null` type is also silently added to the type. In the first code, it is possible to call `foo` without argument, with a `null` or an `A` object. Even when the type is not explicitely nullable. When adding a type to a property, `null`is not automatically added. It has to be explicitely done. Which means that the default value cannot be `null`. To be helpful, PHP checks the default value compatibility for properties and arguments at compilation time, so the feedback is quite fast. The repeated errors where definitely the sign of a change of habit. ## Removing the default value to keep type single The first solution is to remove the default value altogether. In particular, when the property is assigned at constructor time, there is no need to provide a default value. Note that when moving the property to the promoted properties, that property becomes a parameter, and, as such, the hidden `null` type may apply too. ## Cache mechanism with null Now, removing the `null` type is not always possible. In particular, when the default value is needed to set up a caching, or lazy loading mecanism. Here, the constructor will not initialize the property, and the object simply waits for the method to be called once. Then, the argument is cached, and later, reused. The default value `null` is used to detect the empty cache, so it is needed. This is a case where the nullable type is useful. One obvious solution is to add the nullable type to the property. This turns the type is a union type. Indeed, it is a type `A` or `null`. In a sense, union types where available before PHP 8.0, but just for `null`. ## NullPattern alternative An alternative to the usage of `null` is to create an [Null Object Pattern](https://en.wikipedia.org/wiki/Null_object_pattern) for the `A`class, and to use it for default detection. This approach prevents the code to using the `null` type : only an `A` class is needed. It may be tested with `instanceof`, and created anywhere, thanks to the parameterless constructor. On the other hand, it forces the creation of an extra empty class. This class acts as a simple placeholder, and uses more memory than a single `null`. Also, an extra class introduces extra code, albeit a very simple one. This extra class will also prevent usage for the `final` keyword with the class `A`, since `A` class now has a child. And also, every parameter with the `A` type now needs to check for `NullA`, or face mayhem. ## Null or null class? The `nullable` type is a lot easier, and it gives actual value to the `null` value (sic). The `Null` class allows for single typing the property : it comes with some rare edges cases, and adds extra coding to the source. So far, we opted for the nullable version, with its less surprising Fatal Errors. ## Temporary values in properties Let's go back to property typing. We already mentioned that they behave differently than arguments for the type of the default value. They also enforce the type at each step of the life of the property, which means that the property cannot handle temporary values of different types anymore. This is a concern when acquiring data from sources that needs validation. Here is an example: Even after testing the incoming variable `x` as a non-zero positive integer, values in `$_GET` are `string`. This will conflict with the early assignation of `$i`to `$this->int`. With this example, the solution is simple : rewrite some of the `$this->i` with `$i`. This is sufficient. In other situations, the temporary value stays a lot longer before being processed into its final form. Then, the type system forbid that the property hold something else than the expected type, even for a short time. That is one of the most interesting error to catch: it literally cleans the code and makes it a lot more robust. ## More casting values in properties For scalar types, the simplest processing of the temporary value might be a type cast. This would be the case here : This happens a lot with decoded values from JSON or YAML, or incoming values from the Web, (`$_GET`, `$_POST`, ...). This is definitely a side effect of typing with scalars, and it doesn't happen so much when typing with classes. ## Unveiling hidden types Adding class types to properties had a minor annoyance : adding the type in the list of use expressions. In typeless code, it was completely hidden. Now, those types are needed. Look at this code : Obviously, the property `$sqlite3` will now wear a nice `Sqlite3` type : this will avantageously replace the [typing by naming](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#semantic-typing) previous convention. The important part here is to not forget the `use Sqlite3` line at the top of the file, since the namespace is not the global one. Nothing spectacular, and very easy to detect... when one is paying attention. Otherwise, it is easy to add `Sqlite3` as a type, and get an `Unknown class MyNamespace\Sqlite3`for that file. Believe me, it is as obvious as it is easy to forget and curse about it. Also, using an IDE was helpful. In the end, this process makes internally used classes appear in the use list of use expression. This list of `use` is now getting even bigger than previously, and it is turning into a list of dependencies for the class. This might be useful. Another situation that make those types appear is with return types. In the example below, a factory is declared with the expected type. Until now, this return type was not explicitely used in this code. It was hidden. ## resource is not a type This leads to the impossible to type case : `resource`. This is a special and soft reserved keyword : PHP has reserved it, but it is not enforcing it (yet). There is no way to type anything as a `resource`. So, we have this situation: The solution for this specific case was provided by [Tim Bond](https://twitter.com/TimB0nd) : use [SplFileObject](https://www.php.net/manual/en/class.splfileobject.php). This handy little class uses a OOP syntax and exposes all the classic functions, such as fwrite(), fgets(), etc. as methods. There is only a classic `remove the resource and make it a method`rewrite. The call to fopen() is now an instantiation. Note that we also forgot the `use SplFileObject` to make this run : I told you it was easy to forget! And now, the class is typed. ## Not all resources are ready This trick doesn't work with `stream_socket_server()`, which also returns a resource. That resource has no direct alternative, although there might be some solution when looking at the [Sockets](https://www.php.net/manual/en/book.sockets.php) extension. Who knows, since there is already a dedicated `Socket` class. If you have experience with this, please give us a shout! ## Conclusion The journey to 99.9% typing was longer than expected, and it revealed some light traps : - invisible types - Casting more than before - handling the default values - managing temporary values - wrestling with resources - juggling with cache mechanisms Initially, it is easy to postpone typing and only focus on the easy one. Later, typing gets harder, and let some unclean code situation emerge. Dectecting them, thanks to tests, and fixing them along the way is the best way to go. In case of unsolvable typing situation, leaving the property, parameter or return type empty, or `mixed` is a good strategy. The practise shows that such situation tends to simplify itself, by adding the other types in the code. So, just let the hard one on the side, and come back to them later : tightening the code elsewhere do help. Happy PHP code auditing! --- # Let’s make PHP more abstract Source: https://www.exakat.io/lets-make-php-more-abstract/ # Let's make PHP more abstract [`abstract`](https://www.php.net/manual/en/language.oop5.abstract.php) is a conception keyword. It is decided during the conception phase that some classes and some methods uses the `abstract` keyword. Later, that keyword might disappear as needed, yet `abstract` never appears sponteanously. Nobody decides abstraction late in the coding phase. That seems to be a lack of practise rather than a will. So, let's see how it is possible to read a PHP source code, and spot classes and methods that may be abstract. Basically, it means reverting the definition of `abstract` classes and methods, and finding which one fit the constraints. ## Abstract classes An `abstract` class is a class that can't be instantiated. It is the only point in the definition. The class must be inherited by another class, which, in turns can be instantiated. The original class is never instantiated directly. Other usages of the class, such as types, or static call (methods, constants, `::class`, properties) are still valid. The impact of `abstract` on class usage is limited. So, potential `abstract` classes are classes that are never instantiated in the code. Then, adding the keyword to the class definition has other impact at all. In the code below, `A` and `B` can be `abstract`. A note with `abstract` : it may be used with any class in a class hierarchy, except the last one. In other words, an `abstract` class may extend a concrete class. The former must be extended again, before being instantiated. Yet, this is legit code, and it was never see in production. ## Abstract methods Methods may also be `abstract`, although the set of constraints to realize the migration is wider than for classes. When marking a method `abstract`, the method retains its signature, yet looses its body. It then requires every child class to define this method, be marked `abstract` too, or be omitted. As a consequence, the current class needs to be marked `abstract` too. The constraints to make a class `abstract`now apply to the class itself : it mustn't be instantiated on its own. Lastly, the body of the current method shouldn't be used. Besides the inheriting of methods, child method can't make a direct call to the soon-to-be-abstract method : such call prevents its removal. In fact, the method should not be called directly, by its child method or any other method actually. Note that the overwriting method doesn't have to be in a direct child, but may be in a further generation. The intermediate class will require the `abstract` keyword too, if it can. To sum up, methods may be marked `abstract`, when + Their hosting class can be made `abstract` + All the child classes have a local definition of the method, or they can be `abstract` too + The current method is never called directly ## Advantages of abstract classes and methods Abstract classes are an alternative to interfaces. They allow for properties definition, and default implementation for methods. Semantically, an abstract class represents an incomplete object. It is incomplete as it needs extra methods to be able to represent an actual object. In fact, an abstract class cannot be instantiated, so it makes type checks more complete. There is no way anymore for the the incomplete parent class to be provided as a parameter which requires the child class. This would have lead to undefined methods, for example. In fact, with the code above, making the `B` class final will make the types `A`equivalent to `B`. It could simplify the type. `abstract` methods act as a template. They ensure the inherited method have a specific signature, yet do not provide a default implementation. They also apply a strong pressure on the current code, as all children class have to declare that method, and the current class has to be `abstract` too. ## When to go abstract? When reviewing produced code, adding the `abstract` keyword turns an observed code discipline into a enforcable specification. The class that was never instantiated, is now enforced by PHP. As usual, the code reflects reality, not intent. For example, even a specific class that has never been instantiated so far, might be in the future, remote or not. This cannot be infered from the code, and after discussion, it may be better to leave it alone. As a general rule of thumb, it is easier to add the `abstract` keyword to any possible candidate. It makes the current code more tightly organized, and may be dropped later, when needed. ## let's make PHP more abstract Definitely, `abstract` is a conception keyword, that is used by planned code. Yet, it is also interesting to see the `abstract`concept emerge in the source code. A programming feature often emerges from a common coding pattern, and `abstract`is one of them. It is possible to use it before, during and after coding. In fact, 31% of PHP source code could use an average of 5 `abstract` classes. Even when the keyword is eventually set aside, seeing the abstract pattern appear in the source lead to interesting questions about its organisation. And often, it is adopted : `abstract` is easily remembered and taken into consideration when writing code. This approach to add `abstract` to a written PHP source cannot be used with other keywords, such as `final`. `final` prevents a class or a method to be extended : so, it is always the last declaration in the hierarchy. This is too easy to spot, and leads to way too many candidates, or a strict coding convention. --- # PHP 8.1 features one year later Source: https://www.exakat.io/php-8-1-features-one-year-later/ # PHP 8.1 features one year later Closing on one year after PHP 8.1 publication, we can take a look at the published features, and how they are doing in current code. Let's take the list of 'PHP Core' from the [New Features ¶](https://www.php.net/manual/en/migration81.new-features.php). ## Project corpus We'll use that against the Exakat corpus, with 2696 PHP open source projects. Out of those, 1511 were updated since last year, (56%). Those projects had a chance to update their code base to be compatible with PHP 8.1, and then, adopt those features. ## Feature adoption The PHP 8.1 features are all backward incompatible : adopting them means that the code cannot be run anymore on older version, including PHP 8.0. In the case of Open Source projects, with long eras of support, this means that the absolute figures will be much lower than against closed code. The relative adoption of the features shows it popularity and is the most interesting part of this review. That said, let's dive. ## Integer Octal Literal Prefix `014` being equivalent to `0o14` was adopted by 2 projects, including exakat itself for testing purposes. Generally speaking, octals are not common, being used by 34% of the projects, and primarily with mkdir() and related functions. ## Array Unpacking with String Keys This was not tracked by audits so far. It is easily hidden by decoded strings, dynamic code and databases responses. Some research is still needed in this area. ## Named Argument After Argument Unpacking This is not detected in any codes so far. For comparison, named parameters are used by 253 projects (16.7%). ## full-path Key for File Uploads This is not detected in any codes so far. ## Enumerations [Enumeration](https://www.php.net/manual/en/language.enumerations.php) are used by 151 projects (10%). ## Fibers [Fibers](https://www.php.net/manual/en/language.fibers.php) are used by 81 projects, so roughly 5.6 %. ## First Class Callable Syntax [First class callable](https://www.php.net/manual/en/functions.first_class_callable_syntax.php) are used by 201 projects (13.3%). Those are simpler syntax for closures creation. ## Intersection Types Support for intersection types has been added. Intersection types were adopted by 84 projects (5.6 %). Most of them are used with the poster child type of `\Traversable&\Countable`. Other cases are Mock objects (thanks to [Arnout Boks](https://twitter.com/arnoutboks)). # Never type The [`never` type](https://www.php.net/manual/en/language.types.declarations.php) indicates that a function either exit(), throws an exception, or doesn't terminate. Never type were adopted by 43 projects, so roughly 2.9 %. ## new in Initializers New in initializers is the usage of new within PHP static expressions and define() calls for properties, and parameters. New in initializer are used by 241 projects (15.9 %). ## Readonly properties Support for properties that can be written once, then only read has been added. Readonly properties are now in use by 124 projects (8.2 %). ## Final class constants Final keyword is now compatible with constants. They are already used in 14 projects (0.9 %). ## Final round up The top 3 PHP 8.1 features are : - New in initializers 15.9% - First class callable 13.3% - Enumerations 10%. Then, come readonly, intersection type and fiber. Recent statistics place PHP 8.1 adoption between 20% and 30%. With 15.9%, 'new in initializer' is actually adopted half the time, once PHP 8.1 has been upgraded. Enumerations are up to a 33%. These are good levels of adoption, one year later. It seems to me that 'new in initializer' is a very productive feature, with the potential to upgrade significantly our code with the null object pattern. Yet, there are not many tutorials and blogs about them. Did we miss them ? --- # Semantic typing Source: https://www.exakat.io/semantic-typing/ # Semantic typing Semantic typing is an old practice, where the name of the parameter would also tell what its type is. It is typing, because a `$string` is supposed to be typed, and it is semantic, because only the human reader is actually using the meaning : PHP doesn't really care. The interesting part is that the practice of semantic typing still exists nowadays. Obviously, it has taken the backseat to actual typing, for one good reason : naming a parameter with its type is akin to passive documentation, and those who read documentation are too rare. ## Funny typing Funny typing happens when a parameter has a type name, but its type is different. For example, ``` ``` While this is totally legit PHP code, it is also quite weird. Who would call their parameter with one type, yet type it with another one? As usual, when there is a mean, there is a will. So, I ran an audit over 2700+ PHP open source projects, and collected stats about parameters with a scalar name (`$string`, `$int`...), and check their related type. Not PHPDoc type, but an actual associated type. For example, `$string` is typed `string` over 98.3% of the time, but sometimes, just sometimes, it is also typed `array` (1.6%) or `bool`(0.1%). Interestingly, a `$string` is never a float. All other scalar types have the same behavior : they usually bear their eponymous type (`$array` is most often an `array`), but they also carry a different type, such as `bool` or `string`. Interestingly, a `$float` is never an `array`, while a `$bool` is never an `array` (the opposite is not true). Apparently, there are subjective limits to stretching types. This has been made [an exakat rule](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#semantic-typing), with extension to properties. If you are afraid your code might sport such typo, you can run an audit. ## Common parameter names While reviewing those funny typed parameters, I also extracted the most common names for typed parameters. Here are the first 100: - $postBody - $event - $request - $command - $node - $config - $query - $subject - $parent - $item - $requestBody - $result - $context - $value - $entity - $object - $a - $type - $user - $factory - $model - $b - $data - $message - $source - $options - $e - $repository - $response - $client - $field - $other - $manager - $filter - $warning - $container - $collection - $provider - $service - $cache - $extensionAttributes - $file - $action - $element - $dao - $c - $handler - $image - $parser - $validator - $configuration - $resource - $params - $builder - $exception - $services - $target - $metadata - $storage - $connection - $id - $component - $form - $child - $req - $loader - $status - $logger - $entry - $token - $page - $group - $inst - $definition - $document - $input - $template - $table - $error - $rule - $settings - $generator - $registry - $class - $stmt - $repo - $from - $key - $formatter - $reader - $resolver - $category - $controller - $instance - $property - $expected - $helper - $n - $session - $name `$a`, `$b`, `$c`, `$n`, and `$e` are the most common one letter name. `$postBody` is the most common parameter name of all, though being typed does help its ranking : it is not the most common parameter name. Note also that `$requestBody` is ranking high too. Later, `$repository` and `$repo` are both quite often used, and representing the same reality : it's just that the last one is shorter than the former. Also, quite some vague names, such as `$params`, `$value`, `$data`, `$message` or `$source` are used, and type. ## Common varied types Once type is added to a parameter, there is now a new couple in town : the parameter name, with its meaning, and the type itself. As such, it is interesting to look at 2 populations of typed parameters : the one that get a lot of different types, and the one that gets always the same type. ## Always typed the same When a parameter gets the same name and type, across 100 method definitions or more, you can expect semantic typing to be at the root of the behavior: everyone recognize that value, and how it should be represented. Take a look at the list below, which shows the name of a method parameter, and its expected type : can you guess what is that type, simply reading the variable name? Is it obvious that `$weak` should be a boolean? - $allWords (\bool) - $sqlWalker (\doctrine\orm\query\sqlwalker) - $isRoot (\bool) - $replace*extra*symbols (\bool) - $weak (\bool) - $isLower (\bool) - $scriptProperties (\array) - $pathinfo (\string) - $use_transliterate (\bool) - $isVariadic (\bool) - $altNumbers (\bool) - $fkConstraint (\doctrine\dbal\schema\foreignkeyconstraint) - $asOrigReplaceArray (\bool) - $prenormalizeds (\array) - $savePath (\string) - $sessionName (\string) - $httpsPort (\int) - $useAttachment (\bool) - $useShortAttachment (\bool) - $showArguments (\bool) - $httpPort (\int) - $rdata (\array) - $internalErrors (\bool) - $emailLexer (\egulias\emailvalidator\emaillexer) - $arrayAdapter (\symfony\component\cache\adapter\arrayadapter) - $other_keys (\array) - $other_members (\array) - $codePaths (\array) - $enableIfStandalone (\callable) - $extra_args (\array) - $localVault (\symfony\bundle\frameworkbundle\secrets\abstractvault) - $other_values (\array) - $other_args (\array) - $reverseContainer (\symfony\component\dependencyinjection\reversecontainer) - $testMethod (\string) - $wrappedDumper (\symfony\component\vardumper\dumper\datadumperinterface) - $storageKey (\string) - $transportName (\string) - $preloaded (\array) - $hasChild (\bool) - $inlineServices (\array) - $invalidBehavior (\int) - $isNested (\bool) - $byConstructor (\bool) - $lille (\symfony\component\dependencyinjection\tests\compiler\lille) - $maxlifetime (\int) - $maxItemsPerDepth (\int) - $metaBag (\symfony\component\httpfoundation\session\storage\metadatabag) - $cloneArguments (\bool) - $realInstantiator (\callable) - $callAutoload (\bool) - $dumpKeys (\bool) - $callOriginalConstructor (\bool) - $callOriginalClone (\bool) - $hashedPassword (\string) - $utf8 (\bool) - $srcContext (\int) - $sessionOptions (\array) - $keepArgs (\bool) - $autoEtag (\bool) - $autoLastModified (\bool) - $endOfValue (\bool) - $returnResult (\bool) - $isConstructorArgument (\bool) - $includeContextAndExtra (\bool) - $isMatch (\bool) - $pathSeparator (\string) - $noBuiltin (\bool) - $remoteAddr (\string) - $requestUid (\string) - $rightTrimString (\bool) - $refs (\array) - $convertEmptyStringToNull (\bool) - $vault (\symfony\bundle\frameworkbundle\secrets\abstractvault) - $getEnv (\closure) - $dbi (\phpmyadmin\databaseinterface) - $dbForProject (\utopia\database\database) - $willBeAvailable (\callable) - $joinPoint (\neos\flow\aop\joinpointinterface) - $watcherId (\string) - $baseApiUri (\oauth\common\http\uri\uriinterface) - $bookSlug (\string) - $betterNodeFinder (\rector\core\phpparser\node\betternodefinder) - $nodeNameResolver (\rector\nodenameresolver\nodenameresolver) - $nodeTypeResolver (\rector\nodetyperesolver\nodetyperesolver) - $phpDocInfo (\rector\betterphpdocparser\phpdocinfo\phpdocinfo) - $phpDocInfoFactory (\rector\betterphpdocparser\phpdocinfo\phpdocinfofactory) - $reflectionResolver (\rector\core\reflection\reflectionresolver) - $typeKind (\string) - $aliased_classes (\array) - $authComponent (\authcomponent) - $suppressed_issues (\array) - $uniqueName (\string) - $handler_id (\string) - $a_adt (\iladt) - $default_renderer (\ilias\ui\renderer) - $coreRegistry (\magento\framework\registry) - $fetchStrategy (\magento\framework\data\collection\db\fetchstrategyinterface) - $moduleDataSetup (\magento\framework\setup\moduledatasetupinterface) - $telemetryInfo (\phpunit\event\telemetry\info) Parameters with an ending 's' usually leads to an array (`$aliased_classes`, `$sessionOptions`), when the parameter name is a noun. When the parameter name includes a verb, then it is a boolean (`$cloneArguments`, `$dumpKeys`). `boolean` are related to intend, with usage of small words : `$isAbsolute`, `$forConstructor`, `$noBuiltin`). That way, `$willBeAvailable` stands as an exception, being a callable. `string` covers a lot of nouns : `$handler_id`, `$uniqueName`, `$bookSlug`, `$requestUid`, `$hashedPassword` (for that last one, both `password` and `hashed` would also hint at string). A total of 258 parameter names were detected. ## You never know what is in there On the other side of the spectrum, there are parameters which may be, well, basically anything. Some of them have been detected with over a thousand different types, across all their usages. Here is their ranking, by number of different type detected. - $postBody (2352) - $event (1654) - $request (853) - $command (717) - $node (550) - $config (462) - $query (395) - $subject (347) - $parent (302) - $item (300) - $requestBody (300) - $result (299) - $context (294) - $value (290) - $entity (283) - $object (270) - $a (261) - $type (256) - $user (251) - $factory (248) - $model (247) - $b (234) - $data (232) - $message (231) - $source (231) - $options (229) - $e (212) - $repository (211) - $response (207) - $client (198) - $field (197) - $other (191) - $manager (186) - $filter (172) - $warning (171) - $container (169) - $provider (168) - $collection (168) - $service (163) - $cache (162) - $extensionAttributes (161) - $file (154) - $action (153) - $element (152) - $dao (152) - $handler (150) - $c (150) - $image (149) - $parser (146) - $validator (145) - $configuration (144) - $resource (143) - $params (141) - $builder (140) - $exception (137) - $services (135) - $target (134) - $metadata (131) - $storage (130) - $connection (128) - $id (124) - $form (117) - $component (117) - $child (116) - $req (113) - $logger (111) - $status (111) - $loader (111) - $token (110) - $page (110) - $entry (110) - $group (108) - $inst (108) - $definition (107) - $document (107) - $input (106) - $template (106) - $table (103) - $error (102) - $rule (102) - $settings (100) - $generator (97) - $registry (96) - $class (95) - $stmt (95) - $repo (93) - $from (93) - $key (92) - $reader (92) - $resolver (92) - $category (92) - $formatter (92) - $controller (88) - $instance (88) - $expected (88) - $n (88) - $helper (88) - $property (88) - $session (88) - $name (85) To reach 85 types, `$name` had to use more than just scalar types : `string` is expected (at least, by me), but many other classes and interfaces are used, to encapsulate what is a name. Since names are quite a common concept, used to distinguish people, services, brands, models, Debian versions, and else, it is a common that `$name` require disambiguation (dixit Wikipedia). Such parameter names should be avoided: they are quite generic, and may raise questions like : how to I concatenate this name in a string? or other common obvious usage. Indeed, some parameter names are very generic, and lead naturally to many types : `$name`, `$param` (sic), `$instance`, `$collection`, `$factory`, `$entity`, `$other`. `$a`, `$b`, `$c`, and `$n` are back in the list, just like `$e` : this last one is used for exceptions catching, which leads to many different types when propagated to other methods, as a parameter. ## Naming, types and semantics Semantic type establishes a direct relation between types and the words used to build a parameter name. This is an old practice, coming from the early ages of PHP : to keep the code readable, the type is ingrained in the variable name. This was an age with less features than today. Nowadays, types have a life of their own, and yet, this antiquated behavior is still alive. It is possible to guess the type of a variable, simply by reading it aloud. It is also possible to recognize that another variable might have a very wide range of types, and should not be expected to be one or the other. Surely, after discovering which type is actually used, it will be important to read the rest of the documentation to know how to display a simple `$path`: it is not a string. The most common types are definitely the scalar, which are native to every recent PHP version. This analysis covers a vast array of PHP projects, with various backgrounds, including underlying frameworks. Each of them introduce specific classes to support specific concepts, such as URL or names. It would be interesting to see how this semantic typing apply, depending on each communities. --- # Removing duplicate cases in switch Source: https://www.exakat.io/cleaning-switches/ # Removing duplicate cases in switch Switch statements in PHP link a situation (the cases) to code to be executed. Duplicate cases in switch statement should be avoided. Just like this : switch ($a) { case 'a' : /* code here */ case 'b' : /* code here */ default : /* code here */ } ## Duplicate code in switch statements With the size of the switch, it is easy to miss that cases are defined twice. Here is an example in a version of PHPExcel library : switch ($conditional->getStyle()->getFill()->getEndColor()->getRGB()) { case '000000': $colorIdxFg = 0x08; break; case 'FFFFFF': $colorIdxFg = 0x09; break; case 'FF0000': $colorIdxFg = 0x0A; break; case '00FF00': $colorIdxFg = 0x0B; break; case '0000FF': $colorIdxFg = 0x0C; break; case 'FFFF00': $colorIdxFg = 0x0D; break; **case 'FF00FF': $colorIdxFg = 0x0E; break;** case '00FFFF': $colorIdxFg = 0x0F; break; case '800000': $colorIdxFg = 0x10; break; case '008000': $colorIdxFg = 0x11; break; **case '000080': $colorIdxFg = 0x12; break;** case '808000': $colorIdxFg = 0x13; break; case '800080': $colorIdxFg = 0x14; break; case '008080': $colorIdxFg = 0x15; break; /* more cases */ case 'CCCCFF': $colorIdxFg = 0x1F; break; **case '000080': $colorIdxFg = 0x20; break;** **case 'FF00FF': $colorIdxFg = 0x21; break;** case 'FFFF00': $colorIdxFg = 0x22; break; case '00FFFF': $colorIdxFg = 0x23; break; case '800080': $colorIdxFg = 0x24; break; case '800000': $colorIdxFg = 0x25; break; case '008080': $colorIdxFg = 0x26; break; /* more cases */ Two duplicates are in bold, and you can find another five of them. Here, all second case will be ignored and may be dropped, at least from a programming point of view. From a user point of view, may be those 0x20 and 0x21 will be missing at some point. Sometimes, it is quite obvious, as this code from jQuery-File-Upload : switch ($type) { case 'gif': case 'png': imagecolortransparent($new_img, imagecolorallocate($new_img, 0, 0, 0)); case 'png': imagealphablending($new_img, false); imagesavealpha($new_img, true); break; } Here, the 'png' case is twice and will need some refactoring. And sometimes, it is a forgotten copy/paste, as it is in Simplepie  : switch ($this->consume()) { case "\x09": case "\x0A": case "\x0B": case "\x0B": case "\x0C": case "\x20": case "\x3C": case "\x26": case false: /* more code */ } ## What code is prone to duplicate cases ? Legacy statements, long switch and switch with grouped cases. The short ones (1-2 cases) tends to be checked on the fly while coding, so they are pretty safe. ## Is it worth checking duplicate cases? Duplicate cases are pure dead code (only one of them is actually used), or a bug (some of the case is misspelled, and a situation is not taken into account). Finding duplicate case is always rewarded. Switches statements are not too many. Ratio varies from 1 to 2 every thousand of lines of code. This is not a heavy task. ## How to check Finding the switch statements is a search operation. Then, the short ones, with one to 5-7 cases are easy to check visually. Beyond this size and when the cases are too widely separated, it is best extracting the case statement with the IDE, sorting and extracting the duplicates. This is also automatically done with Exakat static auditing tool.   --- # International PHP Conference : PHP 5.6 and static audit Source: https://www.exakat.io/ipc-php-5-6-static-audits/   # International PHP Conference : PHP 5.6 and static audit [![300x250_Speaker_25191](http://www.exakat.com/wp-content/uploads/2014/10/300x250_Speaker_25191.gif)](http://www.exakat.com/wp-content/uploads/2014/10/300x250_Speaker_25191.gif) I'm already busy preparing for the international PHP conference, in Munchen, Germany. I'm giving two talks : on about PHP 5.6 and the second about static audits. Besides, being an impressive conference, I'll be happy to visit one of my first conferences ever. ## Preparing for the next PHP Version Monday, October 27, 2014 - 10:15am to 11:15am With versions stretching from PHP 5.3 to PHP 5.6, PHP has several major published versions, that require special attention when migrating. Beyond checking for compilation, the code must be reviewed to avoid pitfalls like obsoletes functions, new features, change in default parameters or behavior. We'll set up a checklist of such traps, and ways to find them in the code and be reading for PHP 5.6. ## PHP Static Audit Monday, October 27, 2014 - 3:30pm to 4:30pm Even nowadays, PHP code is mostly manually audited. Expert pore over actual code, in search for bugs or code smells. Actually, it is possible to have PHP do this work itself ! Strengthened with the internal Tokenizer, bolstered by the manual, it is able to scan thousands of lines of code, without getting bored, and bringing pragmatic pieces of wisdom: official manual recommendations, version migration, code pruning and security. In the end, it deliver a global overview of the code, without reading it. ## Other interesting sessions - [What job queues may do for you]( http://phpconference.com/2014/en/sessions/queue-it-what-job-queues-can-do-you) - [Regex-fu for PHP](http://phpconference.com/2014/en/sessions/regex-fu-php) - [Dealing with legacy code](http://phpconference.com/2014/en/sessions/dealing-legacy) --- # Upcoming features in PHP 7.1 (part 1) Source: https://www.exakat.io/upcoming-features-php-7-1/ ## Upcoming features in PHP 7.1 The time to meet and greet PHP 7.0 is not so distant, and PHP 7.1 is already knocking at the door ! Since last year, the PHP group has been busy adding features, fixing old ones and keeping the new version as fast as possible : yet, PHP 7.1 managed to have an impressive list of new features. The RFC web site (https://wiki.php.net/rfc) has been voting a lot, and coding even more. Let's see what are the upcoming features of PHP 7.1. This is the first part of our series about last features in PHP 7.1. See ‘[Upcoming features in PHP 7.1 (part a)](https://www.exakat.io/upcoming-features-php-7-1/)‘ and '[More upcoming features in PHP 7.1 (part b)](https://www.exakat.io/upcoming-features-php-7-1-part-b/)' and '[last features in PHP 7.1 (part c)](https://www.exakat.io/last-features-php-7-1-part-c/)' ## list() modernized The venerable [list](http://www.php.net/list)() function (excuse my French, language structure) received a lot of development, and has been upgraded nicely. 'a', 1 => 'b', 2 => 'c'] ; list($a, $b, $c) = $array ; // $a is 'a', $b is 'b', $c is 'c' ?> First, it is now possible to mention offset, and not only rely on integer indexing to assign the values. This saves calling array_values() on the left operand, and other array manipulations to make sure the order of the index are the same as the variables in the list() call. 'a', 1 => 'b', 2 => 'c'] ; list(1 => $b, 2 => $c, 0 => $a) = $array ; // $a is 'a', $b is 'b', $c is 'c' ?> Also, it is possible to use any data container in the list() call : variables, array with offset, properties, array appends or static properties. $this->a, "b" => $this->b, "c" => $this->c ) = $bar; } } ?> Again, this syntax is short, and replace a lot of similar-looking code. Finally, list() itself has been shortened to use the PHP 5.4-introduced Array short syntax. All the goodness above is also available with simple brackets : $this->a, "b" => $this->b, "c" => $this->c ] = $bar; } } ?> It is also possible to nest list() inside other call to list() : destructuring multidimensional arrays ! 1, "y" => 2], ["x" => 2, "y" => 1] ];   list(list("x" => $x1, "y" => $y1), list("x" => $x2, "y" => $y2)) = $points; ?> Finally, one is able to assign several the same value to different variables, simply by mentioning several times the same index, in the left part of the expression : $a, 4 => $b, 4 => $c] = [ 4 => 5]; // $a, $b and $c, all, contain 5  ?> Read the rest of the RFCs ([List keys](https://wiki.php.net/rfc/list_keys) and [short list syntax](https://wiki.php.net/rfc/short_list_syntax)), which have a lot of examples. ## Generalization of negative offset When using many string functions, it is possible to mention a negative offset, that represents an offset starting from the last character of the string. This type of notation has been generalized, and is available to reach any character inside a string : The only place that is left unchanged is array interpolation inside a string : there, a negative number is still considered as a weird expression, and yields a fatal error : Parse error: syntax error, unexpected '-', expecting identifier (T_STRING) or variable (T_VARIABLE) or number (T_NUM_STRING) ## Void return type Void was not a reserved keyword in PHP 7.0, but it managed to join the party, along with int, string, and array, although this is only available as return type, not as argument. The void return type has to be used when a function doesn't return anything. Skipping the return statement, or returning with no argument are valid : by omitting the returned values, the code means that the function has no significant return value. On the other hand, returning null is not accepted as returning void : since the code has an explicit return value, it has another meaning that void. PHP checks for return values at compile time. Using void-returning function in expression is still possible : it would introduce too many backward incompatibilities to be worth enforcing. ## Regex /e option is deprecated (again) It was deprecated in PHP 5.6 for preg_replace, and is it now deprecated for [mb_ereg_replace](http://www.php.net/mb_ereg_replace)  and  [mb_eregi_replace](http://www.php.net/mb_eregi_replace). And ext/mbstring has the same alternative called [mb_ereg_replace_callback](http://www.php.net/mb_ereg_replace_callback), than pcre with [preg_replace_callback](http://www.php.net/preg_replace_callback). It may lack the [preg_replace_callback_array](http://www.php.net/preg_replace_callback_array), though (if you don't know it, go and learn) ## Mcrypt is deprecated « Let's get rid of ext/mcrypt, which is abandonware and inhibits the growth of the language, as soon as humanly possible. », states the RFC. Starting with PHP 7.1, all mcrypt function raise E_DEPRECATED errors, and will be removed the next iteration. If you need cryptography, use the[ ext/openssl](http://www.php.net/openssl). If you really need it, get ready to go to [PECL](http://pecl.php.net/) to compile it. ## More on PHP 7.1 next week That is enough for a first part. We'll be back next week with more on PHP 7.1, covering nullable types, class constant visibility, octal and invalid string arithmetics, and more ! --- # 6 good practices for use in PHP Source: https://www.exakat.io/6-good-practices-for-use/ ## 6 good practices for 'use' in PHP While reviewing code recently, I realized there are no guidelines for[ use](http://php.net/manual/en/language.namespaces.importing.php). Every modern code do use 'use' (sic), as importing classes from composer or a framework is the norm. There are now quite a few variations in syntax for importing classes or traits. Although the impact on performance is low, as use is solved out, having a clean approach to 'use' is important to keep the code clean and readable. Let's review what are the six good usage of use. ## The multiple use expressions Uses configurations come in three flavors : namespaces, traits and closures. Closure have use in its syntax. This usage is strictly formatted, with little variation allowed. Trait use are used inside a class or another trait. It imports a trait inside the current structure. Although PHP doesn't check for multiple import of the same trait, it doesn't have much impact, besides looking weird. Nonexistent trait generate a Fatal error, and that's about it. We'll consider here the namespaces use. They come in three flavors, again : single, multiple or grouped. All flavors have the same configuration purpose on the code. They may also be explicitly aliased or not. ## Best practices with use - Do not import unused classes - Alway use alias - Place all use at first - Avoid using the same alias for different classes - Group use expression for easy reading - Limit the number of use expression ## Do not import unused classes This one is quite obvious, and well known. It applies to all importable structures. PHP doesn't check that all use are used at least once, so there are no mention of useless used. This is mostly dead-code. It is used at compile time, and might slow compilation : this shouldn't be measurable. Dead 'use' doesn't change execution performances. It's good to keep 'use' clean, to avoid confusion or extra cost of maintenance or confusion. ## Always use aliases Any call to a fully namespaced should be avoided, and at least aliased. Firstly, this shorten the code, keep it easier to read. This may also be called 'Avoid long classes name in code'. It also help when refactoring : all namespaces usage are now options of the script. Changing the use expression allows changing all subsequent usage of the alias. This is especially true when the namespace is used several times in the code, saving multiple search and replace in the code. Note that in the long run, it may not be so wise to keep prehistoric names in modern code. Using alias prevent the silent dead code coming from unresolved classes in instanceof, catch() or typehint. Those clauses don't check that the filtered class actually exists. Indeed, when refactoring, classes may easily be changed of namespace or renamed, leaving instanceof orphan, creating dead code. With a well put use, all those would migrate just like other classes. ## Place all use at first in the namespace Uses are expected at the beginning of the file, or the namespace block (They actually impact the namespace, but since most convention is one file, one namespace, it may be confused). In fact, use may be placed anywhere at the root level of the namespace, with significant impact on the readability and usability of the code. Although they are used at compile time, the position at which the 'use' is place has impact of the availability of classes before and after. We won't provide any example, but try it in a safe environment and please, don't burn yourself. ## Avoid using same aliases for different classes Now this one is slightly more controversial, but I'm sure you already got bites by it. It applies to project-wide naming schemes, with the various alias name being used in various files. Imagine that the same name is given to different classes. Those classes may actually be distinct, being located in different namespaces, but once they are aliased, or reduced to their final name, they look alike. It is then easy to mistake one for the other, until the code hits the point where they are not. ## file a.php ## file b.php It is recommended to use aliases names that are specific to one class, and use them consistently across the application. Easy right ? Well, first, if a class (or an interface, or trait) got a name, why would we need another name when actually using them ? Can't we give this 'other' name directly to the class, and then use it straight ? Then, what is the purpose of namespaces again ? Secondly, there are the special case of mock classes, that are meant to replace entirely one class with mocked values or methods. Now, 'use' is exactly made for this, allowing to use the same code with different classes. This may be one exception. Anyhow, it happens often that several classes ends up with the same name in a project. Those would need aliasing, and probably some review anyway. ## Group use expression for easy reading In PHP 5, simply sort the use calls so it's easy to see a family of use, based on the same namespace prefix. In PHP 7, use the grouped expression, to make the prefix obvious. ## Limit the number of use expression below 10 When a file start with a (too) long list of use, it shows that the following code is actually coupled to many other classes. Now, based on rule 'always use alias', measuring coupling is now a matter of counting use commands. [Drupal](http://www.drupal.org/), [octobercms](http://octobercms.com/) and [shopware](https://en.shopware.com/) have up to 30 use in one file, [thelia](http://thelia.net/) has up to 69 (see [here](https://github.com/thelia/thelia/blob/7ffe6521db219205dbf80306be134a62afc30cd3/core/lib/Thelia/Controller/Admin/ProductController.php)). Truthfully, average is between 5 to 8 per file, with higher numbers for Test files. ## Automatically check your code Use expression only impact the current namespace, or file. This makes it quite easy for static analysis tools to review it file by file, and provides feedback on uses based on all the rules above : missing alias, dead alias or aliases that may be reused. The [exakat engine](http://178.62.231.40/) has three specific analysis to deal with those validations. In the long run, keeping use clean and not too frequently used is the most important for your code. Use static analysis to avoid the tedious task to review, count and clean all those use expressions. Thinking about it, this may very well be totally automated ! --- # Exakat 1.9.3 Review Source: https://www.exakat.io/exakat-1-9-3-review/ ![Exakat 1.9.3 Review](https://178.62.231.40/wp-content/uploads/2019/09/cabinet.320.jpg) Exakat 1.9.3 reviews the maximum of your code to PHP 7.4. The [Compatibility PHP 7.4](https://exakat.readthedocs.io/en/latest/Rulesets.html#compatibilityphp74) ruleset now has 20 rules covering the next PHP version. Exakat also checks situations that may generate over 60 different types of PHP messages : remove them and free your logs!  Live, laugh, and Exakat 1.9.3 Review. ## PHP migration review to PHP 7.2 OK, you didn't come here to hear about PHP 7.2. Yet, those versions are still widely used, as mentioned in [Packagist's stats](https://blog.packagist.com/php-versions-stats-2019-1-edition/) : about 25% of all online applications are using PHP 7.1, and 7 % uses PHP 5.6.  Exakat has been doing migration reviews since PHP 5.4, and those are also available. Check the one you need in the [List of Rulesets](https://exakat.readthedocs.io/en/latest/Rulesets.html#list-of-rulesets). ## PHP migration review to PHP 7.4 The real focus of that week was on migration to PHP 7.4. The list of checks rose to 20, with a selection of new verification:  - openssl*random*pseudo_byte() second argument- Deprecated directives- Deprecated constant We also upgraded the Suggestion report with the new features from PHP 7.4, to help you modernize your codes.  ## Error_reporting E_ALL as default for PHP 8 [Nikita Popov](https://twitter.com/nikita_ppv/status/1168492040046415878) announced that PHP will use E_ALL as default value for [error_reporting](https://www.php.net/manual/en/function.error-reporting.php) in PHP 8. This means that the default installation will then report every error that PHP detect while executing an application. Exakat currently includes analysis that report possible error emission for 64 PHP messages. This means that you can review your code to add checks and verification even before running the code, and logging the error message. This is both a gain of speed and a significant boost in code quality. The whole list of errors is available in the documentation annex : [PHP Error messages](https://exakat.readthedocs.io/en/latest/Annex.html#php-error-messages) with the related analysis.  Feel free to report missing error messages to us, so we can see how they can be detected and added. # The Weekly Audits: 2019, Week #36 Exakat includes a 'weekly' report: this report is built with a selection of five analyses. This means a short audit report, with few issues to review. This is not a lot to read them, and review them in your code. Everyone in the PHP community can focus on one of the classic coding problems and fix it. Talk about the weekly audit around you: you'll find programmers facing the same challenges. To obtain the 'weekly' audit, run an audit, and request the 'Weekly' report. # Init the project (skip when it is already done) php exakat.phar init -p -R https://github.com/Seldaek/monolog.git -git # Run the project (skip when it is already done) php exakat.phar project -p # Export the weekly project (every Monday) php exakat.phar report -p -format Weekly # Open projects//weekly/index.html in your browser Every week, you can find here 5 new analysis to review in your code. In fact, when your code is clean, you can also take a quick look at the upcoming analysis.  Weekly recommendations for PHP code review : 2019, week 2019-36 - [Modernize Empty With Expression](https://exakat.readthedocs.io/en/latest/Rules.html#modernize-empty-with-expression) : empty() accept expressions since PHP 5.- [Access Protected Structures](https://exakat.readthedocs.io/en/latest/Rules.html#access-protected-structures) : It is not allowed to access protected properties or methods from outside the class or its relatives.- [No Hardcoded Hash](https://exakat.readthedocs.io/en/latest/Rules.html#no-hardcoded-hash) : Hash should never be hardcoded.- [Never Used Parameter](https://exakat.readthedocs.io/en/latest/Rules.html#never-used-parameter) : When a parameter is never used at calltime, it may be turned into a local variable.- [Should Chain Exception](https://exakat.readthedocs.io/en/lataest/Rules.html#should-chain-exception) : Chain exception to provide more context. # Happy PHP Code Reviews  All the 374 analyzers are presented in the docs, including the narcissic [Identical On Both Sides](http://exakat.readthedocs.io/en/latest/Rules.html#identical-on-both-sides): Operands should be different when comparing or making a logical combination. This is an unusual bug, with more than 19% of chance to appear.  You can check all of the Exakat reports at the gallery: [exakat gallery](https://www.exakat.io/exakat-gallery). Download Exakat on exakat.io, install it with [Docker](https://hub.docker.com/r/exakat/exakat/), upgrade it with 'exakat.phar upgrade -u' and like us on [github](https://github.com/exakat/exakat). --- # PDFF : PHP Document File Format details Source: https://www.exakat.io/pdff-php-document-file-format-details/ # PDFF : PHP Document File Format details In the [first episode](https://www.exakat.io/en/pdff-php-document-file-format/), we have presented the [origin of the PDFF](https://www.exakat.io/en/pdff-php-document-file-format/): how it emerges to be a convenient format to describe PHP component, with a good level of details, some versioning and a dual-readability human/machine. In this second episode, we'll introduce the format and the content of such a file. In the end, there is a repository with a copious amount of PHP components and extensions, ready to be tested. You can get a few of them there to illustrate this document. In the meantime, let's do a in-depth look at the PDFF. ## How : machine and human readable To keep things simple and processable, the underlying format is JSON. This is a format flexible enough for the varied structures that will be stored. The parsers and encoders are wildly available. And the PRETTY/COMPACT presentations provide different presentation for different usages. ## Inside the PDFF : main branches The dataset is represented as a tree. Indeed, this is going to be a big tree. Let's climb the tree. Here are the first levels - name - vcs - handle - versions 0.3.0 \ constants - functions - traits - classes - interfaces - enums - \Zttp\ constants - functions - traits - classes - interfaces - enums At the top, there are some administrative information, including the `name` of the component, the way it was cloned `vcs` and the actual URI to clone it. `handle` may be a URL for `git` or a component identifier for `composer`, for example. Then, the largest field is the `versions` field, which contains the code details. This field contains one object per version, with the version name as property name. Here, it is `0.3.0`. This hash structure allows for several versions in the same file, although one is only displayed. Inside a specific version, the next level are the namespaces. The global namespace `\` is always available, then all the namespaces from the component are detailled, one after the other. The namespaces are not nested, so `\A\`, `\A\B\` and `\A\B\C\` are all distinct entries, at the same level. This is close to PHP's handling of them, and also, different from the storage of files in a file system. For each namespace, all the declared structures are listed, by category. Namely, constants, functions, classes, enumerations, interfaces and traits. This is already quite a large tree. For a small component, there might a few namespaces, but for large frameworks, there may be a over a thousan : Akeneo, Symfony and Shopware, all clock over 1300 namespaces. Now, let's review the different elements. We'll go gradually, introducing the general and specific elements of each category. By the end, there will be some repetition, that will allow us to speed up. ## Global constants Constants are a hash, based on the name of the constant. They have a `name`, and a `value` property, as expected. They also have an array `phpdoc`, for all the phpdoc comments. There are no attributes, as they are not supported by PHP. The `expression` property is a boolean : it is true when the definition of the constant is a static constant expression, or false when it is a literal value. For example, `const A = B + 1;` has a true `expression`, and a piece of PHP code for value. - constants NAME : - name - phpdoc [] - expression - value "constants": { "WP_DEFAULT_THEME": { "name": "WP_DEFAULT_THEME", "phpdoc": [], "expression": false, "value": "'twentytwentytwo'" }, "WP_DEBUG": { "name": "WP_DEBUG", "phpdoc": [], "expression": false, "value": "false" },... ## Functions Functions' description is a bit more complex than constants. In particular, there is a second layer, with parameters. - functions name : - name - returntype - reference - returntypes [] - parameters [] - totalParameters - optionalParameters - variadic - attributes [] - phpdoc [] The name used for index is in lowercase format : it makes it easier to look up functions that way. The actual name, with its casing, is stored in the `name` property. `returntype` is the list of types, returned by the function: they are provided as fully qualified names, all in lower case. This might be an empty array, when no returntype is provided. The type of the returned typehint is stored in the `returntype` property. It may be `one` (single or no type), `or` (union type) or `and` (intersectional type). Parameters are stored in an array of objects, with another level of details. We'll see them in the next section. That array is complemented with the number of `totalParameters` and the number of `optionalParameters`. Functions are also augmented with a `variadic` property: this one is not explicitely expressed in PHP code. It means that one of the arguments (the last, for sure), is a variadic argument, making the whole function callable with an arbitrary number of elements. Finally, `phpdoc` and `attributes`, which collect the corresponding structures from the source code. The attributes are actual PHP code. "functions": { "tap": { "name": "tap", "returntype": "one", "reference": false, "returntypehints": [], "parameters": [... ...], "totalParameters": 2, "optionalParameters": 0, "variadic": false, "attributes": [], "phpdoc": [] } # Parameters Parameters are an extra level of description. They have their own options and descriptions. Parameters are stored as an array. The positions are the actual rank in the function signature, unlink constants and function which use their name as index. The actual description of each parameter has obvious options : `name`, `rank`, `reference`, `variadic`, `phpdoc`, `attributes`, `typehinttype` and `typehints`. Typehints follow the same organisation than for the function return typehints, except for the name itself. Default values for parameters are build around three entries : `hasDefault`, which defines if there is actually a default value or not; that prevents confusion between `null` (no default value) and `null` (default value is null). As for constants, there is an `expression` entry to identify constant static expression in default values. Lastly, `default` is the default value. - parameters - name - rank - variadic - reference - hasDefault - default - expression - typehinttype - typehints [] - phpdoc [] - attributes [] "parameters": [ { "name": "$value", "rank": 0, "variadic": false, "reference": false, "hasDefault": false, "default": "", "expression": false, "typehinttype": "one", "phpdoc": [], "typehints": [], "attributes": [] }, { "name": "$callback", "rank": 1, "variadic": false, "reference": false, "hasDefault": false, "default": "", "expression": false, "typehinttype": "one", "phpdoc": [], "typehints": [], "attributes": [] } ], ## Classes Classes have the largest amount of data : some of them are already described in the previous structures, which we will mention and skip. - classes name - name - final - abstract - readonly - extends - implements [] - traits [] - attributes [] - phpdoc [] - constants [] - properties [] - methods [] Classes are indexed by their name, in lowercase, for easy look up. Their actual name and case are stored in the `name` property. Each class has `abstract`, `readonly` and `final` as boolean attributes; `phpdoc` and `attributes` are similar to the one in functions or parameters. Then `extends` as a single fully qualified name, and `implements` and `uses` as arrays of fully qualified names. All those are the dependencies of the class. `uses` includes the conflict resolutions details (not described here). Then, a class holds arrays of `constants`, `properties` and `methods`. "classes": { "zttp": { "name": "Zttp", "abstract": false, "final": false, "extends": "", "implements": [], "uses": [], "usesOptions": [], "attributes": [], "phpdoc": [], "constants": [...], "properties": [...], "methods": [...] The `constants` array is very similar to the one for global constants, except for the `final` and `visibility` properties. The latter one is a string, with `private`, `protected`, `public` and `none`. The `methods` array is similar to the `functions` one, except for the `static` and `visibility` properties. # Properties The property array is indexed by the property name. There are booleans for `static`, `readonly`; the couple `typehints` and `typehinttype` for typehints; `visibility` string and the triplet `init`, `hasDefault` and `expression` for the initialisation value; and finally the `phpdoc` and `attributes` entries. "$request_type": { "name": "$request_type", "visibility": "protected", "init": "", "static": false, "readonly": false, "hasDefault": true, "expression": false, "typehinttype": "one", "typehints": [], "phpdoc": [ { "phpdoc": "\\/**\n\t * Action name for the requests this table will work with.\n\t *\n\t * @since 4.9.6\n\t *\n\t * @var string $request_type Name of action.\n\t *\\/" } ], "attributes": [] ## Traits Traits are a simpler version of classes. The `uses` entry lists the other traits that are used by the current one, as an array of fully qualified names. - traits name - name - uses - properties - methods - phpdoc ## Interfaces Interfaces are a simpler version of classes. The `extends` entry lists the other interface that is extended by the current one, as a fully qualified name. - interfaces name - name - extends - constants - methods - phpdoc ## Enums Enumerations are a similar to classes, except for the cases and typehints. Typehint are either `string`, `int` or empty. Cases are build similarly to constants. - enums name - name - typehint - constants - methods - cases # Conclusion This quick presentation of of the PDFF format introduced the organisation and the different levels of information stored there. Most of the entries come naturally from the source code, with two exceptions : some extra entries are needed to keep the description acurate, like `typehinttype`, which would be presented as `|`, `&` or `` (nothing). The second difference is that all options are always presented, while PHP code would simply skip them and keep it the source uncluttered. To take a look at this format in more detail, go to the public repository [exakat/pdff](https://github.com/exakat/pdff/tree/main/pdff). In the `vcs` folder, there are frameworks and libraries; in the `packagist` folder, there are components, and in the `ext` folder, there are PHP extensions. Each are detailed per versions. You can download them, and use them as you like. In the next part, we'll review where the PDFF format can help, both for machines and humans. Until then, keep auditing your code! --- # Exakat 1.9.8 Review Source: https://www.exakat.io/exakat-1-9-8-review/ ![](https://178.62.231.40/wp-content/uploads/2019/10/rain.320.jpg)Exakat 1.9.8 review # Exakat 1.9.8 Review This week, Exakat upgrades the presentation of the inventories. It refactored the method definition detection, and added new rule for hiding parameters and wrong case. Several modernisations were applied, in particular to the 'Insufficient typehint' rule. Coding is inherently risky, and it helps to readd the Exakat 1.9.7 review. ## Upgraded Inventories Code inventories represents the code syntaxes of the same kind, gathered in one place. By syntaxes, we mean the same type. That may be if/then, while, foreach, strings, but also incoming variables, dynamic code, regex, MIME types, URL or email.  Consider one moment the regexes : they are usually sprinkled a bit everywhere in the code, and everytime the code suggests using a regex, a new one is written, literally. At the end of the day, there is a lot of reusability that is lost.  With an inventory, all the regex are collected across the code, and are presented at the same place. This makes it easy to spot the four different versions used to extract email addressess from strings, and choose the one that is important.  Exakat collects several inventories, such as : URL, email, regular expressions, MIME types, pack() format, date() format, printf() formats, exceptions and paths. Variables are also presented with the 'Confusing variables' section : there, variables whose name a similar are presented together. Although they may be far away in the code, having to switch from `$xmlconfig` to `$configxml`will add extra burden to any code reviewer. Inventories are available in a dedicated section of the Ambassador report, and as a separate report, called `Inventories`. ## Hiding Parameters Hiding paramter is a new rule with Exakat 1.9.8. It spots arguments which are stored in a local variables, and never used. For example, like this :  Although the example is quite short, it illustrate the situation. The incoming argument is never used, leading to decreased readability.  This is also a case of code moving, or copy/paste : the original code worked, but used othere names for the variables. When copy/pasted to a new location, the easiest is to plug the new argument names in the old one.  It is recommended to simplify such code, and drop the local variable, or rename the argument. In any case, it is worth a short code review. ## Insufficient Typehint Typehinting helps understanding what kind of argument a method needs. Yet, past the argument filtering, there are no check anymore on the usage of the variables. With code aging, it happens that the typehint stays, but the rest of the code changes. In the example here, an interface is used as typehint, but some supplementary methods are used, which are brought by the class. The typehint is insufficient, and should be changed : either by upgrading the interface with the foox() method, or by replacing the i interface with the x class as typehint.  Insufficient typehint also appears when the typehinted object is used to access its properties. Since interfaces don't enforce properties, the typehint doesn't ensure that the following syntax will be valid with arguments. Exakat now also report such shortcoming, and suggests using a class (and even better, an abstract class) as typehint, instead of typehint.  ### Too large typehint ? Too large a typehint is also considered as a rule of analysis : could we find a better suited interface for the current method ? The best would be to check the currently available interfaces, and suggest a smaller interface. This may definitely a job for exakat, so give us a ping if you'd like this feature implemented (on [twitter](https://twitter.com/exakat) or [github](https://github.com/exakat/exakat)). # Wrong Case, anyone? ### Functions with wrong case Wrong case is a code analysis which reports any application whose name is spelled differently than it was defined. For example,  This code is legit, since PHP is case-insenstivite when it comes to methods and functions. It won't affect your code, whatever the case it use. Yet, it may be important to [coding conventions](https://exakat.readthedocs.io/en/latest/Rulesets.html#coding-conventions) : there is a specific exakat rulesets which reports such violations. ### Methods with wrong case The case insentivity also applies to methods. With Exakat 1.9.8, this analysis was upgraded to include methods, both static and normal.  Class constants and properties are processed not covered with this analysis, since they are case-sensitve : code right, or get a NULL! # The Weekly Audits: 2019, Week #41 Exakat includes a 'weekly' report: this report is built with a selection of five analyses. This means a short audit report, with few issues to review. This is not a lot to read them, and review them in your code. Everyone in the PHP community can focus on one of the classic coding problems and fix it. Talk about the weekly audit around you: you'll find programmers facing the same challenges. To obtain the 'weekly' audit, run an audit, and request the 'Weekly' report. # Init the project (skip when it is already done) php exakat.phar init -p -R https://github.com/Seldaek/monolog.git -git # Run the project (skip when it is already done) php exakat.phar project -p # Export the weekly project (every Monday) php exakat.phar report -p -format Weekly # Open projects//weekly/index.html in your browser Every week, you can find here 5 new analysis to review in your code. In fact, when your code is clean, you can also take a quick look at the upcoming analysis.  Weekly recommendations for PHP code review : 2019, week 2019-41 - [Use Basename Suffix](https://exakat.readthedocs.io/en/latest/Rules.html#use-basename-suffix) : basename() will remove extension when it is provided. - [Strange Name For Constants](https://exakat.readthedocs.io/en/latest/Rules.html#strange-name-for-constants) : Those constants looks like a typo from other names. - [Useless Parenthesis](https://exakat.readthedocs.io/en/latest/Rules.html#useless-parenthesis) : Situations where parenthesis are not necessary, and may be removed. - [Property Could Be Local](https://exakat.readthedocs.io/en/latest/Rules.html#property-could-be-local) : A property only used in one method may be turned into a local variable. - [Queries In Loops](https://exakat.readthedocs.io/en/latest/Rules.html#queries-in-loops) : Avoid querying databases in a loop. # Happy PHP Code Reviews  All the 385 analyzers are presented in the docs, including the mobile : [Avoid Parenthesis](http://exakat.readthedocs.io/en/latest/Rules.html#avoid-parenthesis): Avoid Parenthesis for language construct. This is an unusual bug, with more than 10% of chance to appear.  You can check all of the Exakat reports at the gallery: [exakat gallery](https://www.exakat.io/exakat-gallery). Download Exakat on exakat.io, install it with [Docker](https://hub.docker.com/r/exakat/exakat/), upgrade it with 'exakat.phar upgrade -u' and like us on [github](https://github.com/exakat/exakat). --- # Classic Code Review for PHP Classes Source: https://www.exakat.io/classic-code-review-for-php-classes/ ![](https://178.62.231.40/wp-content/uploads/2019/07/organ.320.jpg) ## Classic Code Review for PHP Classes Classes help organise the code by breaking it into smaller components, that combined together to build powerful applications. Property definitions, abstract classes, local variables and identical methods are all code smells that evolve directly inside large repositories with many classes.  Here some common code smells that arise in OOP PHP code. They all may be spotted with Exakat, the static analysis engine for PHP.  ## Define All the Properties PHP is flexible as ever, and there is no need to declare a property before using it.  Yet, it helps PHP to know in advance that a property will be used. It takes advantage of it by pre-allocating memory, and reusing it faster. The property is ready as soon as it is needed. Compare that to the fallback behavior, where PHP has to search for the property, fail to find it, then allocate the memory. This also helps reuse the memory for new objects, as they now have a comparable and predictable footprint.  Quick benchmarks will show that defined properties are 50% faster than undefined properties. It is a micro-optimisation, as accessing a property is very fast, but given the amount of usage of properties, including in loops, this is usually worth the work.  As a common rule, make sure all your [properties are defined](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#undefined-properties). As a side note, it also helps your analysis tools. In fact, as an extra tip, you can add the following __get() and __set() to all your classes, including the one which are totally defined and don't use magic functions. With them, when an unknown property is called during execution, the magic methods are called, and will act as canaries to warn you about a typo or a mis-target. Use them during development and testing phase, then remove them for production.  ## Could Be Abstract Class An abstract class is a class that can't be instantiated. It must be a parent of a concrete class, which may be instantiated. Besides holding static methods and class constants, this is the only option for it to be useful.  So how do you call a class which is extended and never instantiated directly? Well, that looks like a template for the inheriting classes, and may actually be marked as such by [using the abstract keyword](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#could-be-abstract-class).  This will actually prevent the class to be instantiated, yet have a constructor. Note that there is no need for any method to be abstract : the whole class may be abstract, implement interfaces, and yet, have no abstract methods. Abstract classes won't be tested, at least directly. When you want to keep those tests, extend this abstract class into an empty concrete class. Then, test this class. The behavior will be the same, but now, there is a special class, distinct from any implementation.  ## Property Could Be Local What is the family relationship between a property and a local variable ? Both can hold values, but the scope is different. A local variable is restricted to a method scope, while a property is available within a class, across several methods (let's omit the public properties for a moment). With those definitions, local variables and properties are similar but different. In fact, the only moment when they are identical is when they cumulate the constraints : a [property that is used in only one method](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#property-could-be-local).  Such property could be turned into a local variable, without impacting the rest of the class.  The property may be used across several calls to the same method : in that case, it may be interesting to move it to a static variable.  There is also another interesting situation : a local variable that is available in multiple methods. To achieve this, the local scope must transmit the local variable to another scope by calling another method as an argument. This is how variables that are local start appearing in multiple methods signatures, until one realize it could be upgraded to a property, and removed from those signatures. ## Identical Methods In A Class Tree It is usually easy to spot identical methods in one class : the name itself is usually enough to yield a « Fatal error ». On the other hand, [identical methods in a class](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#identical-methods) tree is harder to find. That is, harder to find for us, humans. The same method, available in both the parent class and the child class, is strangely difficult to spot. They look the same, and they also belong to different files, while serving the same purpose. If one overwrites the other, it may be bad luck. Identical methods in a class tree is a copy/paste code smell. One of the classes was copied from the other during its creation, or some generalizing refactoring stopped short, and left methods in multiple position.  The good aspect is that it is easy for static analysis to find them. As long as they are identical, and not modified.  The choice of the remaining method may be tricky : is it a parent method, available to other children ? Or is it a child’s specific method ? Or even, should the child method overwrite the parent on purpose, to cancel it?  ## Could Use Self Usually, classes are identified with their fully qualified name, such as `\X`, `\A\B\C\MyClass`, or even an alias `MyClass`. And just like anything that is hardcoded, it is hard to change it without refactoring all its instances.  Whenever the class references itself with a FQN, [the special name 'self'](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#could-use-self) (or its close cousin 'static' for Late State Binding), should be used. It keeps the FQN relative to the class, and it is still readable. It makes any refactoring or renaming easier.  Note that self also represents any parent class, so you may use it even when referencing a structure defined in parents.  ## Disconnected Classes Let's finish with a special structure : the [disconnected classes](https://exakat.readthedocs.io/en/latest/Reference/Rules.html#disconnected-classes). This happens in class hierarchies, when a class extends another class. The situation is that both classes keep living their life, separated : they never call the other one. One extreme case is when the parent class provide support for the child class. This usually saves repeating code, provide formal structure and help plug the child class in a larger framework. In this case, the child class make use of the parent class.  The other extreme case is when the parent classes act as a template for child classes, which provides specialisation, such as methods or properties. The parent has some generic code that is configured thanks to the child configuration. The parent class calls the child. The general case is a hybrid version of the two above, where parent and children exchange calls to each other. In the end, this build one integrated class, that is a subtle mix of both initial classes. This almost sounds like genetics. So, what happens when two classes have an extends relationship, but keep from calling each other ? This ends as a code smell, where one of the classes is trying to fulfill a mission in the wrong place of the code. It is a good start for a refactorisation. ## Happy Code Review Most of those patterns happen with the organic evolution of the code. They are the reason why code review is important in the first place : it takes some experience and time to read the code base and spot strange construct in the classes or in the control flow. All those errors are available in the '[Class Review](https://exakat.readthedocs.io/en/latest/Reference/Rulesets.html#classreview)' rule set, in the Exakat engine. [Exakat](https://www.exakat.io/) is a static analysis tool, that review PHP code for code quality and self-improvement. It is [open source](https://exakat.readthedocs.io/en/latest/Introduction.html#what-is-exakat), easy to [install](https://exakat.readthedocs.io/en/latest/Installation.html) and may run on any PHP code base. --- # Setting exceptions at the right place Source: https://www.exakat.io/setting-exceptions-at-the-right-place/ [![](https://www.exakat.io/wp-content/uploads/2019/09/relocation.320.jpg)](https://www.exakat.io/wp-content/uploads/2019/09/relocation.320.jpg) ## Exceptions at the line [Exceptions](https://www.php.net/manual/en/language.exceptions.php) are configured with the file and line where they were thrown. This is the classic situation : the exception is thrown immediately where a problem is detected.  ## Exceptions at a wrong line It is not always convenient to throw the exception in the code. In particular, when a new layer of code is added to handle common operations. After all, this is what functions and classes are for. Here, the validation is exported to a separate function, so as to avoid repeating (and forgetting) the various exceptions. One significant drawback of this situation is that validateData() now centralizes all the exceptions. Every detected exception is thrown from this particular function. Fatal error: Uncaught Exception: Id is missing id for Smith in /tmp/test.php:9 Stack trace: #0 /tmp/test.php(3): validateData(...) #1 {main} thrown in /tmp/test.php on line 9 ## A message to Exception There is a solution to this problem : creating a custom exception. Exceptions, and all its native tree, are a good source of base exceptions. Most of the time, they are customized with a new message :  Yet, the exception classes include a lot of properties, including file and line number.  This way, the exception may be designing another place in the code than the one where the exception was thrown :  The result looks like this :  Fatal error: Uncaught Exception: Id is missing id for Smith in myfile.php:1000 Stack trace: #0 /tmp/test.php(3): validateData(...) #1 {main} thrown in /tmp/test.php on line 9 ## The right exception at the right place You may notice that we only changed the file and line number with hard-coded values. This is not really helpful, since it doesn't designate any real code. Using the [magic constants](https://www.php.net/manual/en/language.constants.predefined.php) `__FILE__`and `__LINE__` leads to the same problem : it designates the current code, not the calling code. The calling code, and its predecessors, is available with a call to debug_backtrace(). This native function builds the stack of calls that lead to the current execution. It returns an array of arrays : each contains the file, line, function or method, class and type of call.  Here is the array, when [debug_backtrace()](https://www.php.net/manual/en/function.debug-backtrace.php) is called with the `DEBUG_BACKTRACE_IGNORE_ARGS`. Array ( [0] => Array ( [file] => /tmp/test.php [line] => 9 [function] => __construct [class] => myException [type] => -> ) [1] => Array ( [file] => /tmp/test.php [line] => 3 [function] => validateData ) ) We can now access the file and line we'd like to display :  The resulting default error message is now pointing to useful coordinates. It may be personalized further with [__toString()](https://www.php.net/manual/en/language.oop5.magic.php#object.tostring) or a [getMessage()](https://www.php.net/manual/en/exception.getmessage.php) call, in the try/catch. Fatal error: Uncaught Exception: Id is missing id for Smith in /tmp/test.php:3 Stack trace: #0 /tmp/test.php(3): validateData(...) #1 {main} thrown in /tmp/test.php on line 9 ## Wrapping up Exceptions may be configured beyond simple messages : it is possible to customise the file and line number, and the message displayed when the exception is used with echo. This is much more convenient than the default behavior.  Don't forget to [chain exceptions](https://www.php.net/manual/en/exception.construct.php) : always relay the previous exceptions to the newly created one, so that the whole chain of exceptions is available to the last receiver. --- # Most Popular PHP Magic Methods Source: https://www.exakat.io/most-popular-php-magic-methods/ [![](https://www.exakat.io/wp-content/uploads/2019/09/magic.320.jpg)](https://www.exakat.io/wp-content/uploads/2019/09/magic.320.jpg) # Most Popular PHP Magic Methods PHP features the concept of magic methods : methods that have a special function. They are related to other PHP features, and are automatically called on an object, when available.  For example, the __toString() method is called whenever an object is converted to a string : this may be with a type cast, a call to echo or print, or a concatenation. When such method is not available, a default behavior is used. There are no less than 15 magic methods in PHP. :  - [__construct](http://www.php.net/__construct) - [__destruct](http://www.php.net/__destruct) - [__call](http://www.php.net/__call) - [__callStatic](http://www.php.net/__callStatic) - [__get](http://www.php.net/__get) - [__set](http://www.php.net/__set) - [__isset](http://www.php.net/__isset) - [__unset](http://www.php.net/__unset) - [__sleep](http://www.php.net/__sleep) - [__wakeup](http://www.php.net/__wakeup) - [__toString](http://www.php.net/__toString) - [__invoke](http://www.php.net/__invoke) - [__set_state](http://www.php.net/__set_state) - [__clone](http://www.php.net/__clone) - [__debugInfo](http://www.php.net/__debugInfo) ## PHP Magic Methods We polled a corpus of 1705 Open Source applications, to check their respective usage of magic methods. Anytime a project defines, at least once, a magic method, it is counted.  ![Most popular PHP magic methods](https://178.62.231.40/wp-content/uploads/2019/09/magic_method.stats_-1024x594.png)PHP most popular magic methods ## A few notes - All magic methods are quite popular. This is a widely used concept. - The most common magic method is `__construct()`, by far. It is almost compulsory for a class, yet it is surprisingly low in usage, at 63%. - `__toString()` is quite popular - `__debugInfo()` was introduced in PHP 5.6 - `__get()` is more popular than `__set();`__sleep()`is more popular than`__wakeup()`.  - `__destruct()` is only used half the time of `__construct()`.  - `__serialize()` and `__unserialize()` are only available in PHP 7.4. Either those projects are already compatible, or they will have to refactor some code. --- # PHP Typehint usage in 2019 Source: https://www.exakat.io/php-typehint-usage-in-2019/ ## Overall Typehint Usage [PHP typehints](https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) were introduced in PHP 5. Ever since, they have grown in features and usage. Where are we at the moment ? We scanned the Open Source community repositories. 89% of all PHP code source use types hinting, at least a little.  ![](https://178.62.231.40/wp-content/uploads/2019/11/Typehinting-usage.png) ## Typehint Coverage This number is very high, yet it represents very different situations. Here is a breakdown of the actual coverage of parameters and return type. The graph below presents the % of parameters and return type that are explicitly defined in a code source.  This graph goes from 0% to 100%. This reflects a wide range of situations. The average project has 11 000 parameters and return type to assign, and this may be much higher for the largest projects : up to 170 000 typehints. Yet, the size is no indicator of typehint coverage.  ![](https://178.62.231.40/wp-content/uploads/2019/11/typehint-coverage-1024x636.png) The average is currently 19%, while the median is 11%. This means that type hinting efforts are still in the beginning.  Several bumps are currently hampering the adoption of typehint : + multiple type as argument + error management (by returning multiple types) + class extensions and common interfaces, where adding a type hint means adding it simultaneously to several methods + multiple arguments + arrays, where their content should be typed There are solutions, already available or coming, to help with all those situations, so those numbers should change in the coming years. ## Used typehints 67% of all the typehint are scalar typehints. This shows that typehints are currently used to enforce the existing situation. The methods have been receiving integers as argument, and it was easy to set them as typehint. This is probably the middle step : no typehint, then scalar typehint, then class/interface typehint.  ![](https://178.62.231.40/wp-content/uploads/2019/11/typehint-style-1024x639.png) ## Measures All figures above were produced by running an [exakat](https://www.exakat.io/) audit on 815 open source applications. --- # Exakat 2.2.3 is out ! Source: https://www.exakat.io/exakat-2-2-3-is-out/ # Exakat 2.2.3 is out ! The latest release of Exakat is out. It adds support for PHP 8.1 at the Exakat engine level : the new syntax are now recognized, and represented in the internal graph database. This covers enumerations, final constants, first class callable syntax, octal representation and readonly properties. All those are represented as detailled features and ready to be used for analysis. In this version, there is a new report for PHP 8.1 migration, and 5 new analysis : PHP 8.1 removed directives and constants, duplicate named arguments, htmlentities() change of default option and wrong named parameters for PHP native functions. The full list of change is available in the changelog ([https://www.exakat.io/en/exakat-changelog/](https://www.exakat.io/en/exakat-changelog/)) and the details about analysis are in the manual ([https://exakat.readthedocs.org/](https://exakat.readthedocs.org/)). 2.2.3 is an enterprise version. Community edition is 2.1.9. --- # 5 usages of static keyword in PHP Source: https://www.exakat.io/5-usages-of-static-keyword-in-php/ # 5 usages of static keyword in PHP Static is a PHP keyword with many usages. It is almost universally used, though there are many variations of it. Let's review all the five of them : - static method - static property - static closure - static variable - static as a classname ## Static method The most common usage of static is for defining [static methods](http://php.net/manual/en/language.oop5.static.php#language.oop5.static.methods). Such methods are part of a class, just like any method, though they may be used even without any such instantiated object. This is actually the main difference with a normal method : `$this` is not available in such methods. Note that static methods may be called from an object, though the pseudo-variable `$this` will still NOT be available. In fact, PHP emits a fatal error, whenever the `$this` variable is directly mentioned in a static method : this is why the example has to use the variable variable to actually show the status. Since static methods do not depends on an object, they are great for factories. They are also useful to access the private static properties, since both of them are static. Finally, they are also useful for utils that are related to a class but not depend on local object state : however, those may be considered as a function. ## Static properties [Static properties](http://php.net/manual/en/language.oop5.static.php#language.oop5.static.properties) are the close cousin of static methods. They do not depend on the object, but only on the class itself. They may hold some important information for the class, but not for the objects. Static properties have several niche usages. The first of them is being a property for a static method. That makes sense : anything a static method has to store must be in a static property. Actually, a private one, or a protected. They play the role of constants when the value of the constant is dynamically loaded, and thus, are not known at compile time. Non-class constants may be defined at execution time with `define`, but not class constants : thus, the usage of static properties. They also replace constants when the constant has to be a resource or an array or another object. They are used when implementing a singleton, when the object instantiation has a prerequisite, or to share data between objects, such as a counter. They may also be used for local cache. Static properties tend to be difficult to initialize : all properties may be initialized at coding time (default value), or construct time. Obviously, the latter is not available for static property, and only the default value is possible. The other option is to set the static property in the main code, at bootstrap, leading to some difficult to solve dependencies. ## static closure While methods may be static or not, function has no such alternative : they just can't be static. Yet, there is one final type of method that may be static : [static closures](http://php.net/manual/en/functions.anonymous.php). Closure are functions that may be stored in a variable : functions may have their name stored in a variable, though. Closure also have the ability to aggregate variables from the context of their creation, for future use. As such, `$this` is available in a closure that is created inside an object. Note that there is no need for 'use ($this)' when creating the closure to include `$this` in it : it is automatically done. Now, if the `foo` method is actually static, then `$this` is not available anymore, nor for the static method, nor for the created closure. But if you don't want to propagate the context of creation with the closure, you may actually make the closure `static` and `$this` won't be available anymore. This may avoid leaks, or will allow the `$this` variable to be filled by another context later. ## Static variables Just like there are static methods and static functions, there are both static properties AND [static variables](http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static). Static variables are variables that stay within a context, and are available next call. This actually works like some prehistoric class : the static variable is available to store some cached data : here, we have used it as counter, but static variables makes great cache for any dictionary that shouldn't be loaded each call. Load the dictionary at the first call, then it will reside in memory for the rest of the execuction. Note that the variable is completely invisible from the outside world, just like a private property. Usually, static variables are a first step before making this function a full class : once you understand that testing such unaccessible variable requires some 'reset' mechanism, its prehistoric charm wans and the function is refactored as a class. ## static as a classname It is not possible to call a class `static` but it is well possible to refer to it with this keyword. Within a class body, you may refer to the current class, or any of its parent in case of fallback, using `static`. That is convenient in a surprising number of situations. When using static in those situations, `static` will be dynamically replaced by the current acting class. In the example, it is the x class. When you change the name of the class from x to something else, the rest of the class keeps running : `static` acts as a relative class name (relative to the code location, that is). There is another keyword in competition with `static` : it's `self`. Together, they handle the [Late Static Bindings](http://php.net/manual/en/language.oop5.late-static-bindings.php). Basically, `self` may just be used at the same location, and it will behave almost identically. Except that `self` is resolved at compile type, 'statically' if you pardon the pun, and it will always reference the class it is written in. While `static` depends on who is calling the static entity. Confused ? On the first call to whoami(), the context of calling is X, so the method displays `X X`. On the second call to whoami(), Y is used, but whoami is actually located in X, as Y has no such method. There, `static` and `self` parts ways, and display different classes. In terms of performances, always default to `self`, as it will be compiled, and executed as such. `static` is far rarely used. ## Review your code with static analysis Static analysis has no relation with the `static` keyword. Yet, it may very wel help you review your code, and find usage of those features. - static method : [avoid using](http://exakat.readthedocs.io/en/latest/Rules.html#this-is-not-for-static-methods) `$this` in those methods, and consider adding static to methods that doesn't use this variable. - static property : [make `static` `private`](http://exakat.readthedocs.io/en/latest/Rules.html#property-could-be-private-property), when the property is only used internally. - static closure : add `static` when the closure has no usage of the `$this`. - static variable : when the `static` variable is not taught, it is often [build from `global`](http://exakat.readthedocs.io/en/latest/Rules.html#could-be-static) that is restricted to one function. Spot those, and make them `static` or turn the whole code into a class. - static as a classname : whenever the current class is mentioned within itself, think about using `self` or `static` for added comfort. --- # Exakat 1.9.4 Review Source: https://www.exakat.io/exakat-1-9-4-review/ # ![](https://178.62.231.40/wp-content/uploads/2019/09/orange.320.jpg)Exakat 1.9.4 review Exakat 1.9.4 has a new harvest of new code reviews to make PHP code better and better : rules for PHP 7.4, whose [Compatibility PHP 7.4](https://exakat.readthedocs.io/en/latest/Rulesets.html#compatibilityphp74) ruleset now has 21 rules; rules for backward compatibility; inventory of structures nesting and dimensions for arrays; rules for ?? and . precedence. We also introduced a new report, 'Stubs'.  There are two motives for reading the Exakat 1.9.4 review; one, that you enjoy it; the other, that you can boast about it. ## Quick Run for PHP 7.4 Compatibility By default, Exakat runs all necessary rules for generating the [Ambassador](https://exakat.readthedocs.io/en/latest/Reports.html#ambassador) report. And, sometimes, Exakat must run only for one special aspect, such as migration to PHP 7.4.  There are two places to configure the expected reports : `command line` and `config.ini`.  - `command line`: when invoking exakat with the `project` command, you may use the `-format` option to request any special report. It is possible to use the option multiple times, to produce several reports. php exakat.phar project -p exakat -format Migration74 - `config.ini`: in the `projects//config.ini` file, add the `project_reports[]` line, with the name of the report. Repeat the process for each report. With this configuration, there is no need to use the `-format` option in command line ;projects//config.ini project_reports[] = 'Migration74'; In command line : `php exakat.phar project -p exakat` Since Ambassador is the largest report available, running one single audit with a smaller report is faster. Yet, Ambassador also comes with the ability to produce other reports, even when the audit is finished; for smaller reports, this is not always the case. The documentation has the [full list of reports](https://exakat.readthedocs.io/en/latest/Reports.html#).  ## Coalesce and Concatenation precedence Coalesce was introduced in PHP 7.0, with the operator `??`. It will also be upgraded to an assignment operator in PHP 7.4, with `??=`.  Coalesce is a way to provide a default value, when a variable is not set. For example, here, `$a` is created with the value of `$b`, and if `$b` is not available (read `null`),  This is a very convenient way to ensure a variable always have a valid value.  Problems happen with the same expression is used in combination with concatenation. Such as this ;  There are two fallacies here : the first is that `$b` is used by the coalesce operator. In fact, since concatenation has a higher precedence, `'a' . $b` is actually used. And this is never `NULL`, because of the literal string. So, the second part of the operator is never used, and this is mostly a strange `@` operator (`??` hides errors while it checks the variable). The second part of the coalesce is also a fallacy here : it is not `'b'`, but `'b' . 'c'`. Again, concatenation has precedence, and the second argument of the coalesce is actually `'bc'`, even if it is build with a concatenation. To check it, just try the following code :  This will assign `$b` or `'bcde'` to `$a`, and nothing else. In doubt, use parenthesis to ensure that the expected order in the expression is the right one. Or avoid combining concatenation and coalesce operators : it will be longer to write, but safer. ## How Deep Is Your Code? [Object Calisthenics](https://williamdurand.fr/2013/06/03/object-calisthenics/) recommends no more than one level of indentation. No nested if then, or nested loops. I meet companies which are enforcing a strict level of 2 maximum. [Others differs](https://stackoverflow.com/questions/491668/levels-of-indentation), and set the bar at 4 or 5.  So, how deep is your code ? Exakat 1.9.4 introduces two features to track them : first, Max Level Of Nesting, which spots methods, functions, closures or arrowfunctions (coming with PHP 7.4), with more than 4 levels of nesting. All structures that introduce a new sequence are noted : if..then, for, foreach, do..while, while, switch. And a closure is always distinct of its parent method. The acceptable level of nesting may be configured by using the maxLevel parameter. 4 levels are quite large, and will only catch the worst offenders. Just like for the number of arguments, it is the natural limit where most coders will start wondering if it is reasonable. You may very well decide to use a lower level, as a coding convention. If you're still wondering how deep is your code, you'll get a panoramic view of it with the Ambassador report (Exakat's default report) : in the section 'Audit logs', a new section was added with indentation levels. It shows the repartition of code lines, by levels.  This will help decide what level of nesting is reasonable for your application.  # The Weekly Audits: 2019, Week #37 Exakat includes a 'weekly' report: this report is built with a selection of five analyses. This means a short audit report, with few issues to review. This is not a lot to read them, and review them in your code. Everyone in the PHP community can focus on one of the classic coding problems and fix it. Talk about the weekly audit around you: you'll find programmers facing the same challenges. To obtain the 'weekly' audit, run an audit, and request the 'Weekly' report. # Init the project (skip when it is already done) php exakat.phar init -p -R https://github.com/Seldaek/monolog.git -git # Run the project (skip when it is already done) php exakat.phar project -p # Export the weekly project (every Monday) php exakat.phar report -p -format Weekly # Open projects//weekly/index.html in your browser Every week, you can find here 5 new analysis to review in your code. In fact, when your code is clean, you can also take a quick look at the upcoming analysis.  Weekly recommendations for PHP code review : 2019, week 2019-37 - [Multiply By One](https://exakat.readthedocs.io/en/latest/Rules.html#multiply-by-one) : Multiplying by 1 is a fancy type cast.- [Implicit Global](https://exakat.readthedocs.io/en/latest/Rules.html#implicit-global) : Global variables, that are used in local scope with global keyword, but are not declared as global in the global scope.- [Could Use Try](https://exakat.readthedocs.io/en/latest/Rules.html#could-use-try) : Some commands may raise exceptions.- [Unresolved Use](https://exakat.readthedocs.io/en/latest/Rules.html#unresolved-use) : The following use instructions cannot be resolved to a class or a namespace.- [Identical Conditions](https://exakat.readthedocs.io/en/latest/Rules.html#identical-conditions) : These logical expressions contain members that are identical. # Happy PHP Code Reviews  All the 374 analyzers are presented in the docs, including the ordinary [Multiply By One](http://exakat.readthedocs.io/en/latest/Rules.html#multiply-by-ones): Multiplying by 1 is a fancy type cast. This is an unusual bug, with more than 17% of chance to appear.  You can check all of the Exakat reports at the gallery: [exakat gallery](https://www.exakat.io/exakat-gallery). Download Exakat on exakat.io, install it with [Docker](https://hub.docker.com/r/exakat/exakat/), upgrade it with 'exakat.phar upgrade -u' and like us on [github](https://github.com/exakat/exakat). --- # Exakat 1.9.5 Review Source: https://www.exakat.io/exakat-1-9-5-review/ ![](https://178.62.231.40/wp-content/uploads/2019/09/dunes.320.jpg)Exakat 1.9.5 review Exakat 1.9.5 includes analysis that prevents code from decaying, or warn you early about it : useless type checks thanks to Typehint, non-implemented interfaces, incompatible signatures with PHP 7.3, wrong expectations with interface typehints. Also, PHP 7.4 migration was upgraded with the upcoming change of bit shift precedence versus concatenation. And, Ambassador reports typehints for methods, and all magic property used.  The Exakat 1.9.5 is beautiful because it doesn’t last. ## Useless Type Checks With Typehints One of the tactical reasons to adopt typehints everywhere is to hand the checks to PHP. Instead of calling is_array(), one may simply use `array` as a typehint. While it is obvious in this example, it also happens that some comparisons are also hidden. They may be hiding deep into the method, or be nested, or even depends on other arguments before being carried.  When such situation happens, adding a typehint too hastily to the signature of the method will lead to dead code : some of the conditions will never be valid anymore.  Exakat now uses the typehint provided at the method level, and checks when the remaining comparisons and type checks are always constant : either valid, or invalid. Those are then reported as a promising refactoring issue. ## Non-implemented Interfaces Non-implemented interface is one of those typical error that may happen in production, while linting is only providing green lights. In fact, when interfaces and classes are stored in separate files, PHP doesn't load them to check one another. As such, changing the interface without changing the class leads to no-compile-time error.  Obviously, this will lead to a runtime error. It would be best caught by unit tests, or any code execution that makes use of the code. Though, it may also be code that is on the way out, or is less and less used. Exakat makes a systematic check of the code, and reports those as Dead code.  Upgrade it or remove it : that's a motto for those. ## Incompatible Signatures in PHP 7.3 and 7.4 Incompatible signature is the fact that PHP checks when overwritten methods have the same signature. Same number of arguments, same typehint, same usage of reference. On the other hand, the name of the argument and its default value could change.  Until PHP 7.3, it was a strict comparison, or a drop : you could remove a typehint, but not change it. In PHP 7.4, the notion of covariance and contra variance was introduced : argument typehint may be compatible with the parent's typehint. In particular, the parent's typehint maybe a child class of the child's typehint. This is the contra variance of arguments : with arguments, the typehint change in opposite direction. The covariance is for the return value : the parent's return value must be a parent of the return value of the child. Return type hint must change in the same direction. ### The banana, the gorilla and the jungle This leads to this new error message, as displayed in the example above : "Could not check compatibility between xx::bar(B $a) and foo::bar(A $a), because class A is not available". At runtime, PHP has to check if the B typehint is compatible with the A type. It needs to load the classes or interfaces B and A, to see if one implements or extends the other.  It is going to be an interesting change in the behavior of the code : until now, with a one-for-one check, PHP never loaded any class when loading one of them : either the type hint is A, either it is not. In every case, there is no need to load the class to check. Only the parent classes are needed. Nowawady, loading one class means loading the parent classes, but also the type hinted classes (when method signature disagree), and their parent. At that point, if those classes matches, their signatures will be checked again, leading to a new load of classes, and potentially, other classes.  Check below : the class foo() in `xx` requires B and A to be loaded, then C and D, which may require other classes... You want the banana, you get the whole regimen, the gorilla and then the whole jungle... One point to keep in mind : with the above example, loading xx leads to a lack of information on C and D. With split files, this is going to be difficult to debug. Covariance and contra variance are completely unavailable until PHP 7.4 : PHP 7.3 only accepts identical typehint, which will be the default fallback if you need to keep your code backward compatible, or want to make those pesky error message go away.  ### Self is not what you believe Yet, if PHP 7.3 is the safe behavior, it also has its own range of shortcomings. Here is one of them, that contra variance will remove. Class A, which indeed implements i, cannot return `self`, because i also returns self. "Declaration of A::foo($a): A must be compatible with i::foo($a): i ". PHP 7.3 only consider `self`to be the current class, and doesn't check its implemented interfaces. Later, i is also using `self`for its return type, leading to the error. PHP 7.4 removes this error. All the hard work of the PHP core group on the notions of covariance and contra variance lead us to split the `incompatible signature` analysis in two : one before PHP 7.4 and one after PHP 7.4.  ## Wrong Expectations With Interfaces The best practice goes as "Do not use classes for typehint, use interfaces". A class is a concrete implementation, and the typehint creates a hard dependency between the current method and another class. What will happen when this class must evolve to a new version or implementation? The good behavior is to use an interface, which leads to multiple implementation.  There is already an analysis for [No Class As Typehint](https://exakat.readthedocs.io/en/latest/Rules.html#no-class-as-typehint). Then, this conflicts with the popular usage of public properties : an interface doesn't specify any properties, but only constants and methods.  At that point, any class implementing I must have a public property called `p`. Better options would be to add a getter in the interface, to read the value, and make the property private, though it may only shift the problem to another place.  Whatever the solution, Exakat detects usage of interfaces that go beyond the method and constant usage. Maybe using the class, or a parent class is still the best choice here, until the property is privatized.  Also, [No Class As Typehint](https://exakat.readthedocs.io/en/latest/Rules.html#no-class-as-typehint) has been upgraded to avoid suggesting interfaces when it is conflicting with this analysis. ## More inventories for Ambassador Report The Ambassador report now includes 2 new inventories : the inventory of magic properties and the inventory of typehint.  Both those two sections are available in the [Ambassador](https://exakat.readthedocs.io/en/latest/Reports.html#ambassador) report. ### Inventory of magic properties Magic properties are used when a class implements `__get` and `__set` methods, allowing the calling script to use on the fly properties. A large number of those properties are detected by Exakat : such properties are undefined, and depend on object whose class implement the magic methods above.  When such a link is established, Exakat reports all magic properties in the 'Used magic properties' inventory. The name of every property is reported, giving the opportunity to review their spelling or their distribution. In upcoming versions, details about usage (read and write), and originating class will be included in that report. Ping us if you want that with a passion! ### Inventory of type hints With the upcoming advent of type hinted properties, it seemed necessary to add an inventory of typehint usage in Exakat. This is the first take with exakat 1.9.5!  The inventory of type hints list all classes and their methods and arguments, with their respective return typehint and typehint, and their default value.  Next version will include property typehints, and then, we'll start including to typehint suggestions : based on the usage of the argument or the return value, and other contextual elements, it is possible to identify a large number of typehints. Stay tuned! # The Weekly Audits: 2019, Week #38 Exakat includes a 'weekly' report: this report is built with a selection of five analyses. This means a short audit report, with few issues to review. This is not a lot to read them, and review them in your code. Everyone in the PHP community can focus on one of the classic coding problems and fix it. Talk about the weekly audit around you: you'll find programmers facing the same challenges. To obtain the 'weekly' audit, run an audit, and request the 'Weekly' report. # Init the project (skip when it is already done) php exakat.phar init -p -R https://github.com/Seldaek/monolog.git -git # Run the project (skip when it is already done) php exakat.phar project -p # Export the weekly project (every Monday) php exakat.phar report -p -format Weekly # Open projects//weekly/index.html in your browser Every week, you can find here 5 new analysis to review in your code. In fact, when your code is clean, you can also take a quick look at the upcoming analysis.  Weekly recommendations for PHP code review : 2019, week 2019-38 - [Ambiguous Static](https://exakat.readthedocs.io/en/latest/Rules.html#ambiguous-static) : Methods or properties with the same name, are defined static in one class, and not static in another.- [One Variable String](https://exakat.readthedocs.io/en/latest/Rules.html#one-variable-string) : These strings only contains one variable or property or array.- [Too Many Native Calls](https://exakat.readthedocs.io/en/latest/Rules.html#too-many-native-calls) : Avoid stuffing too many PHP native call inside another functioncall.- [Use Named Boolean In Argument Definition](https://exakat.readthedocs.io/en/latest/Rules.html#use-named-boolean-in-argument-definition) : Boolean in argument definitions is confusing.- [Constant Class](https://exakat.readthedocs.io/en/latest/Rules.html#constant-class) : A class or an interface only made up of constants. # Happy PHP Code Reviews  All the 384 analyzers are presented in the docs, including the guitly [Undefined Variable](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-variable): Variable that is used before any creation. It is a frequent bug : 77% of all projects are affected. You can check all of the Exakat reports at the gallery: [exakat gallery](https://www.exakat.io/exakat-gallery). Download Exakat on exakat.io, install it with [Docker](https://hub.docker.com/r/exakat/exakat/), upgrade it with 'exakat.phar upgrade -u' and like us on [github](https://github.com/exakat/exakat). --- # Exakat 1.9.7 Review Source: https://www.exakat.io/exakat-1-9-7-review/ ![](https://178.62.231.40/wp-content/uploads/2019/10/coffee.320.jpg) This week, Exakat added a new inventory for too many chained object. The engine upgraded the storage of Typehint and Default values, which lead to several modernisation and coverage extensions, such as 'typehint are for interface or abstract classes', and 'Never used parameters'.  Code is like a series of baby steps, and a good Exakat 1.9.7 review. ## Too Many Chained Objects Chaining objects is a classic sight in any PHP code. To reach a resource across the code, one need to call several methods :  `Main::get('root')->getUsers($id)->getAddress(self::Current)::$zip_code->format();` Exakat 1.9.7 builds an inventory of objects dereferencing depths. This gives an interesting overview on the application itself. Then, a new analysis was introduced : Max deferencing. It reports any expression that is chaining too many methods and properties. By default, a maximum of 7 dereferencing is set, which may be set to other levels, stricter or not. ![Dereferencing levels charts](https://178.62.231.40/wp-content/uploads/2019/10/dereferencing-levels.png) In particular, the first reports showed that such writing style is quite popular, and may lead to really high chaining : chains 35 to 65 calls are possible. This is due to fluent interfaces, in particular for queries.  We'll work on detecting those fluent interfaces, and distinguish them from the other chaining call in a next version. ## TYPEHINT and DEFAULT values Until now, Exakat represented typehint and default values as they appear in PHP : an option. They may be available, or not. This lead to extra manipulations, to handle the situations where those values are not available.  With the evolution of PHP, those options are more and more present. To speed up the process, typehints and default values are always available, though they may use the 'Void' object, making them point to nothing. It acts as a Null Pattern : supporting the syntax, but doing about nothing. We'll extend the coverage to properties, just like in PHP 7.4. Until the code itself start using the new typed properties, Exakat will fill in the blanks, detecting automatically the type of the property, and checking its related usage. ## No class as typehint Typehints on methods should always use interface : the relation between a method and its calling context should be based on a contract, more than a concrete class.  Interfaces do not allow specifications of properties, only methods and constants. And yet, 45% of PHP applications use public properties from an argument inside a method.  To keep close to the letter of the law, `abstract classes` are the closest structure we can use to both specify methods and properties, while typehinting with a contract and not a concrete class.  This has been updated in Exakat 1.9.7. # Never Used Parameter 'Never used parameter' is a rule which reports parameters which are never used to call a function. The Function foo() was defined with two arguments, $a and $b. $b has a default value, and may be skipped when called. While the definition of the second argument may have made sense at the function definition, it appears that the function usage always skips this argument, and rely only on its default value. As such, the argument is never used. What to do with such argument ? As usual, it may be useful to keep it, for future use. Or, one may consider that an unused parameter is also dead code : remove it, and hard code it in the foo() function until it is actually useful. There is no need for more complexity. This analysis was introduced in Exakat 1.8.3 : it now also covers method calls, statics or normal.  # The Weekly Audits: 2019, Week #40 Exakat includes a 'weekly' report: this report is built with a selection of five analyses. This means a short audit report, with few issues to review. This is not a lot to read them, and review them in your code. Everyone in the PHP community can focus on one of the classic coding problems and fix it. Talk about the weekly audit around you: you'll find programmers facing the same challenges. To obtain the 'weekly' audit, run an audit, and request the 'Weekly' report. # Init the project (skip when it is already done) php exakat.phar init -p -R https://github.com/Seldaek/monolog.git -git # Run the project (skip when it is already done) php exakat.phar project -p # Export the weekly project (every Monday) php exakat.phar report -p -format Weekly # Open projects//weekly/index.html in your browser Every week, you can find here 5 new analysis to review in your code. In fact, when your code is clean, you can also take a quick look at the upcoming analysis.  Weekly recommendations for PHP code review : 2019, week 2019-40 - [list() May Omit Variables](https://exakat.readthedocs.io/en/latest/Rules.html#list-may-omit-variables) : Simply omit any unused variable in a list() call.- [Randomly Sorted Arrays](https://exakat.readthedocs.io/en/latest/Rules.html#randomly-sorted-arrays) : Those literal arrays are written in several places, but their items are in various orders.- [Class, Interface Or Trait With Identical Names](https://exakat.readthedocs.io/en/latest/Rules.html#class-interface-or-trait-with-identical-names) : The following names are used at the same time for classes, interfaces or traits.- [Useless Check](https://exakat.readthedocs.io/en/latest/Rules.html#useless-check) : Situation where the condition is useless.- [Missing Cases In Switch](https://exakat.readthedocs.io/en/latest/Rules.html#missing-cases-in-switch) : It seems that some cases are missing in this switch structure. # Happy PHP Code Reviews  All the 386 analyzers are presented in the docs, including the mobile : [Repeated print()](http://exakat.readthedocs.io/en/latest/Rules.html#repeated-print()): Always merge several print or echo in one call. This is an common bug, with more than 49% of chance to appear.  You can check all of the Exakat reports at the gallery: [exakat gallery](https://www.exakat.io/exakat-gallery). Download Exakat on exakat.io, install it with [Docker](https://hub.docker.com/r/exakat/exakat/), upgrade it with 'exakat.phar upgrade -u' and like us on [github](https://github.com/exakat/exakat). --- # Removing a value in an array, six methods Source: https://www.exakat.io/removing-a-value-in-an-array-six-methods/ # Removing a value in an array, six methods Removing one value is a classic task, and it is quite natural to discover multiple ways to do so. Then, you can choose the one that suits your coding style the best. Usually, alternatives algorithms come with different limitations : speed, memory usage, extensions... In this article, we'll focus on the following set up : We want to remove the letter `b` in the alphabet. Any other letter should be able to replace `b`, though we expect it to be available in that list, and only once. The availability will allow the skipping of existence tests, while they should be explicit in real code. Here are the six alternatives : - array_search() - array_diff() - array_filter() - array_keys() - array_flip() - foreach() In fact, array*flip(), foreach, array*filter() have double, though they are not different enough to appear twice in this list. Let's go! ## array_search() array_search() looks for the key associated to a value in an array. With this key, we can then remove the index in the array. Here, we also know that the key will be found, as we know it is in that list : real code would add a check to skip unset() when the id is not found. ## array_diff() array_diff() removes all the values in the first array that are present in the second array. Here, since we are looking for a single value, that second array is a small array. Kudos to the fact that zero, one or more values are removed from the initial array : this works even if the value is not available. Also note that a new array is produced : the original array is not modified. ## array_filter() array*filter() is the older cousin of array*diff() : instead of making a direct comparison between values in two array, it applies a closure on each element, and keep the one that are true. Here, the closure is very simple, but in real life, it is convenient for complex calculations. Closure, use expression and arrow functions are all possible here. ## array_keys() array_keys() has a second arguments that is rarely used : it is a filter value, that limits the returned keys to the one that hold that value. This is exactly what we need here, lest one detail : there is only one to remove. Then, we can get that first key straight at index 0. array*keys() then works a bit like array*diff(), but with an extra step. ## array_flip() [array_flip](https://www.php.net/array_flip)() flips an array : keys becomes values, and values becomes keys. Which means that instead of searching for 'b' in the values, we can now use it as an index, and get it location in the original array. array_flip() is often a shiny toy with index and values manipulations, though it often comes with caveats. In this article, values being strings and unique, it works quite well. In case there are multiple identical values, all but one will be lost. And in case some of the strings look like a number, such as '1', or '1e3', there will be type juggling. Knowing that, it works. ## foreach() Last, but not least, is our good friend foreach(). Often, foreach() is faster than a function call, as all of this happens in the same context. In this case, there might be a lot of copying or checking, so that advantage tend to be limited. Two alternative : either spot the value in the code and remove it, or rebuild the whole array. ## Speed checks The amount of code to write is illustrated in the code above, so you can make up your mind about which will save you more keystrokes than the other. As for speed, here is the final ranking, over a million runs, and 26 elements in the array : - array_search() : 81.59 mseconds - array_diff() : 217.00 mseconds - array_flip() : 220.48 mseconds - array_keys() : 226.73 mseconds - array_flip() (twice) : 298.21 mseconds - foreach() (unset) : 465.73 mseconds - foreach() (rebuild) : 665.78 mseconds - array_filter() : 919.68 mseconds - array_filter() use : 1,057.45 mseconds - array_filter() fn : 1,044.57 mseconds All of them are a micro-optimisation, although it is interesting to see the difference in required processing time to reach the same result. Another interesting take is that some of those algorithms will not adapt easily to multiple 'b' in the array, or when removing multiple distinct values ['b', 'e', 'p']... Evolutivity may be important. array*splice() could also have been used, instead of unset(), when that call is explicit. Though, array*splice() is not performant on such small size, so it was left out of this article. --- # EPIC : Exakat PHP Index of Coding (June 2020) Source: https://www.exakat.io/epic-exakat-php-index-of-coding-june-2020/ # EPIC : Exakat PHP Index of Coding (June 2020) Not using @ is the poster child of good practices. It's also looked upon, as an impossible goal. Did you know that the @ operator is only merely used by 50% of PHP applications ? Same for parenthesis with include and co : don't use them, like 50% of the developpers. This is how the Exakat PHP Index of coding was born. Every month, Exakat runs thousands of analysis on half a million lines of PHP code. This is primarily for testing purpose, a kind of torture test that checks the engine run on any kind of code. And it is very useful to ensure all situations are correctly handled. We also extracted the following stats out of 1700+ projects, analysis by analysis. This way, any issue may be ranked from 'wide spread' to 'very unusual'. In fact, 'wide spread' may also be understood as : 'almost a feature'. May be we can suggest a few of them to wiki.php.net. Each analysis is ranked below, with its frequency of appearance in code, its progression. If you want to test your own code, just install exakat and run an audit. | Jun 2020 | May 2018 | Prog. | Name | Rating | Change | | -------- | -------- | ----- | ---- | ------ | ------ | | 1 | 1 | | [Uses Default Values  ](http://exakat.readthedocs.io/en/latest/Rules.html#uses-default-values) | 97.50 % | 2.84 % | | 2 | 2 | | [Used Once Variables (In Scope)  ](http://exakat.readthedocs.io/en/latest/Rules.html#used-once-variables-in-scope) | 95.98 % | 4.51 % | | 3 | N/A | | [Method Could Be Static  ](http://exakat.readthedocs.io/en/latest/Rules.html#method-could-be-static) | 95.61 % | 95.61 % | | 4 | N/A | | [Class Could Be Final  ](http://exakat.readthedocs.io/en/latest/Rules.html#class-could-be-final) | 95.55 % | 95.55 % | | 5 | 5 | | [Should Use Local Class  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-use-local-class) | 94.94 % | 4.36 % | | 6 | 39 | | [Bail Out Early  ](http://exakat.readthedocs.io/en/latest/Rules.html#bail-out-early) | 92.02 % | 29.24 % | | 7 | 11 | | [Undefined Classes  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-classes) | 91.59 % | 12.87 % | | 8 | 8 | | [PHP Keywords As Names  ](http://exakat.readthedocs.io/en/latest/Rules.html#php-keywords-as-names) | 89.95 % | 7.62 % | | 9 | 19 | | [Unused Arguments  ](http://exakat.readthedocs.io/en/latest/Rules.html#unused-arguments) | 89.09 % | 16.02 % | | 10 | 14 | | [Nested Ifthen  ](http://exakat.readthedocs.io/en/latest/Rules.html#nested-ifthen) | 87.69 % | 10.70 % | | 11 | 17 | | [Used Once Variables  ](http://exakat.readthedocs.io/en/latest/Rules.html#used-once-variables) | 87.08 % | 13.03 % | | 12 | 9 | | [Unresolved Classes  ](http://exakat.readthedocs.io/en/latest/Rules.html#unresolved-classes) | 86.96 % | 6.67 % | | 13 | 26 | | [Preprocessable  ](http://exakat.readthedocs.io/en/latest/Rules.html#preprocessable) | 86.35 % | 16.64 % | | 14 | 18 | | [Should Make Ternary  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-make-ternary) | 85.99 % | 12.03 % | | 15 | 10 | | [Property Used In One Method Only  ](http://exakat.readthedocs.io/en/latest/Rules.html#property-used-in-one-method-only) | 84.53 % | 5.72 % | | 16 | 22 | | [No Boolean As Default  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-boolean-as-default) | 84.28 % | 12.42 % | | 17 | 23 | | [Use Named Boolean In Argument Definition  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-named-boolean-in-argument-definition) | 84.15 % | 12.54 % | | 18 | 15 | | [Relay Function  ](http://exakat.readthedocs.io/en/latest/Rules.html#relay-function) | 83.61 % | 7.71 % | | 19 | 27 | | [Avoid Optional Properties  ](http://exakat.readthedocs.io/en/latest/Rules.html#avoid-optional-properties) | 83.19 % | 13.95 % | | 20 | 24 | | [Buried Assignation  ](http://exakat.readthedocs.io/en/latest/Rules.html#buried-assignation) | 82.82 % | 11.75 % | | 21 | 69 | | [Uncaught Exceptions  ](http://exakat.readthedocs.io/en/latest/Rules.html#uncaught-exceptions) | 81.66 % | 30.15 % | | 22 | 31 | | [Use Positive Condition  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-positive-condition) | 81.54 % | 13.69 % | | 23 | 20 | | [Useless Parenthesis  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-parenthesis) | 81.05 % | 8.20 % | | 24 | 41 | | [Iffectations  ](http://exakat.readthedocs.io/en/latest/Rules.html#iffectations) | 80.32 % | 18.12 % | | 25 | 40 | | [Switch To Switch  ](http://exakat.readthedocs.io/en/latest/Rules.html#switch-to-switch) | 79.23 % | 16.92 % | | 26 | 160 | | [Written Only Variables  ](http://exakat.readthedocs.io/en/latest/Rules.html#written-only-variables) | 78.62 % | 52.06 % | | 27 | 46 | | [No Class In Global  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-class-in-global) | 78.62 % | 17.64 % | | 28 | 29 | | [Overwritten Literals  ](http://exakat.readthedocs.io/en/latest/Rules.html#overwritten-literals) | 78.56 % | 9.72 % | | 29 | 44 | | [Assigned Twice  ](http://exakat.readthedocs.io/en/latest/Rules.html#assigned-twice) | 78.31 % | 16.91 % | | 30 | N/A | | [Undefined Variable  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-variable) | 77.51 % | 77.51 % | | 31 | 25 | | [Strict Comparison With Booleans  ](http://exakat.readthedocs.io/en/latest/Rules.html#strict-comparison-with-booleans) | 76.85 % | 5.92 % | | 32 | 37 | | [Could Make A Function  ](http://exakat.readthedocs.io/en/latest/Rules.html#could-make-a-function) | 75.57 % | 11.51 % | | 33 | 35 | | [No Need For Else  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-need-for-else) | 74.96 % | 10.37 % | | 34 | 32 | | [Pre-increment  ](http://exakat.readthedocs.io/en/latest/Rules.html#pre-increment) | 74.60 % | 8.11 % | | 35 | 38 | | [Use Class Operator  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-class-operator) | 74.60 % | 11.41 % | | 36 | 58 | | [include_once() Usage  ](http://exakat.readthedocs.io/en/latest/Rules.html#include_once-usage) | 73.69 % | 17.66 % | | 37 | N/A | | [Inconsistent Elseif  ](http://exakat.readthedocs.io/en/latest/Rules.html#inconsistent-elseif) | 73.06 % | 73.06 % | | 38 | 36 | | [Used Once Property  ](http://exakat.readthedocs.io/en/latest/Rules.html#used-once-property) | 72.89 % | 8.67 % | | 39 | 12 | | [Unused Classes  ](http://exakat.readthedocs.io/en/latest/Rules.html#unused-classes) | 72.77 % | -5.53 % | | 40 | 55 | | [Dont Change The Blind Var  ](http://exakat.readthedocs.io/en/latest/Rules.html#dont-change-the-blind-var) | 72.28 % | 14.94 % | | 41 | 42 | | [Never Used Properties  ](http://exakat.readthedocs.io/en/latest/Rules.html#never-used-properties) | 72.04 % | 10.38 % | | 42 | 70 | | [Undefined Functions  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-functions) | 71.98 % | 20.65 % | | 43 | 56 | | [Empty Function  ](http://exakat.readthedocs.io/en/latest/Rules.html#empty-function) | 71.86 % | 14.83 % | | 44 | 73 | | [Property Could Be Local  ](http://exakat.readthedocs.io/en/latest/Rules.html#property-could-be-local) | 71.78 % | 21.34 % | | 45 | 131 | | [Should Use Coalesce  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-use-coalesce) | 71.07 % | 36.87 % | | 46 | 53 | | [Drop Else After Return  ](http://exakat.readthedocs.io/en/latest/Rules.html#drop-else-after-return) | 70.46 % | 12.91 % | | 47 | N/A | | [Ambiguous Visibilities  ](http://exakat.readthedocs.io/en/latest/Rules.html#ambiguous-visibilities) | 70.26 % | 70.26 % | | 48 | 50 | | [Check All Types  ](http://exakat.readthedocs.io/en/latest/Rules.html#check-all-types) | 68.75 % | 10.60 % | | 49 | 66 | | [Switch Without Default  ](http://exakat.readthedocs.io/en/latest/Rules.html#switch-without-default) | 68.39 % | 16.28 % | | 50 | 30 | | [Long Arguments  ](http://exakat.readthedocs.io/en/latest/Rules.html#long-arguments) | 67.23 % | -1.49 % | | 51 | 51 | | [Undefined Interfaces  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-interfaces) | 67.11 % | 9.04 % | | 52 | 62 | | [@ Operator  ](http://exakat.readthedocs.io/en/latest/Rules.html#@-operator) | 66.80 % | 13.43 % | | 53 | 76 | | [Logical To in_array  ](http://exakat.readthedocs.io/en/latest/Rules.html#logical-to-in_array) | 66.26 % | 16.66 % | | 54 | 72 | | [Unresolved Use  ](http://exakat.readthedocs.io/en/latest/Rules.html#unresolved-use) | 65.89 % | 15.08 % | | 55 | 54 | | [Else If Versus Elseif  ](http://exakat.readthedocs.io/en/latest/Rules.html#else-if-versus-elseif) | 65.46 % | 7.96 % | | 56 | N/A | | [Could Be Abstract Class  ](http://exakat.readthedocs.io/en/latest/Rules.html#could-be-abstract-class) | 65.38 % | 65.38 % | | 57 | 67 | | [Static Loop  ](http://exakat.readthedocs.io/en/latest/Rules.html#static-loop) | 65.22 % | 13.63 % | | 58 | 200 | | [Too Many Local Variables  ](http://exakat.readthedocs.io/en/latest/Rules.html#too-many-local-variables) | 65.04 % | 45.86 % | | 59 | 96 | | [Ambiguous Static  ](http://exakat.readthedocs.io/en/latest/Rules.html#ambiguous-static) | 64.96 % | 21.26 % | | 60 | N/A | | [Check JSON  ](http://exakat.readthedocs.io/en/latest/Rules.html#check-json) | 64.47 % | 64.47 % | | 61 | 308 | | [Non Ascii Variables  ](http://exakat.readthedocs.io/en/latest/Rules.html#non-ascii-variables) | 64.06 % | 62.76 % | | 62 | 71 | | [Undefined Parent  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-parent) | 63.21 % | 11.99 % | | 63 | 77 | | [Use Instanceof  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-instanceof) | 63.03 % | 13.64 % | | 64 | 87 | | [Mismatched Ternary Alternatives  ](http://exakat.readthedocs.io/en/latest/Rules.html#mismatched-ternary-alternatives) | 62.72 % | 16.25 % | | 65 | 163 | | [Hardcoded Passwords  ](http://exakat.readthedocs.io/en/latest/Rules.html#hardcoded-passwords) | 61.44 % | 35.15 % | | 66 | 74 | | [Common Alternatives  ](http://exakat.readthedocs.io/en/latest/Rules.html#common-alternatives) | 61.44 % | 11.63 % | | 67 | 80 | | [Missing Include  ](http://exakat.readthedocs.io/en/latest/Rules.html#missing-include) | 61.36 % | 12.65 % | | 68 | 59 | | [No Parenthesis For Language Construct  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-parenthesis-for-language-construct) | 61.20 % | 5.38 % | | 69 | 84 | | [No Public Access  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-public-access) | 60.84 % | 13.22 % | | 70 | 89 | | [Could Use Short Assignation  ](http://exakat.readthedocs.io/en/latest/Rules.html#could-use-short-assignation) | 60.84 % | 14.90 % | | 71 | 176 | | [Useless Abstract Class  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-abstract-class) | 60.53 % | 36.12 % | | 72 | 63 | | [Exit() Usage  ](http://exakat.readthedocs.io/en/latest/Rules.html#exit-usage) | 60.35 % | 7.14 % | | 73 | 83 | | [Use random_int()  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-random_int) | 59.86 % | 11.98 % | | 74 | 114 | | [Useless Instructions  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-instructions) | 58.70 % | 21.64 % | | 75 | 65 | | [Echo With Concat  ](http://exakat.readthedocs.io/en/latest/Rules.html#echo-with-concat) | 58.70 % | 6.56 % | | 76 | 91 | | [Undefined Properties  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-properties) | 58.58 % | 13.63 % | | 77 | 82 | | [Identical Consecutive Expression  ](http://exakat.readthedocs.io/en/latest/Rules.html#identical-consecutive-expression) | 58.43 % | 10.50 % | | 78 | 68 | | [Empty Classes  ](http://exakat.readthedocs.io/en/latest/Rules.html#empty-classes) | 58.40 % | 6.89 % | | 79 | 90 | | [String May Hold A Variable  ](http://exakat.readthedocs.io/en/latest/Rules.html#string-may-hold-a-variable) | 58.22 % | 12.83 % | | 80 | 93 | | [Avoid Substr() One  ](http://exakat.readthedocs.io/en/latest/Rules.html#avoid-substr-one) | 57.97 % | 13.49 % | | 81 | N/A | | [Variable Is Not A Condition  ](http://exakat.readthedocs.io/en/latest/Rules.html#variable-is-not-a-condition) | 57.52 % | 57.52 % | | 82 | 78 | | [Several Instructions On The Same Line  ](http://exakat.readthedocs.io/en/latest/Rules.html#several-instructions-on-the-same-line) | 57.42 % | 8.29 % | | 83 | 124 | | [Could Use self  ](http://exakat.readthedocs.io/en/latest/Rules.html#could-use-self) | 57.42 % | 22.45 % | | 84 | 88 | | [Useless Referenced Argument  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-referenced-argument) | 57.40 % | 11.25 % | | 85 | 100 | | [Empty Blocks  ](http://exakat.readthedocs.io/en/latest/Rules.html#empty-blocks) | 57.36 % | 15.18 % | | 86 | 109 | | [Double Instructions  ](http://exakat.readthedocs.io/en/latest/Rules.html#double-instructions) | 57.30 % | 17.32 % | | 87 | 94 | | [Use const  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-const) | 57.00 % | 12.57 % | | 88 | 95 | | [Useless Interfaces  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-interfaces) | 57.00 % | 13.15 % | | 89 | 127 | | [Could Use __DIR__  ](http://exakat.readthedocs.io/en/latest/Rules.html#could-use-__dir__) | 56.69 % | 22.12 % | | 90 | 123 | | [Modernize Empty With Expression  ](http://exakat.readthedocs.io/en/latest/Rules.html#modernize-empty-with-expression) | 56.33 % | 21.15 % | | 91 | 102 | | [No array_merge() In Loops  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-array_merge-in-loops) | 55.96 % | 14.18 % | | 92 | 110 | | [Parent First  ](http://exakat.readthedocs.io/en/latest/Rules.html#parent-first) | 55.57 % | 15.59 % | | 93 | 106 | | [Double Assignation  ](http://exakat.readthedocs.io/en/latest/Rules.html#double-assignation) | 55.42 % | 14.91 % | | 94 | 115 | | [Cast To Boolean  ](http://exakat.readthedocs.io/en/latest/Rules.html#cast-to-boolean) | 55.42 % | 18.47 % | | 95 | 57 | | [Undefined Constants  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-constants) | 54.99 % | -1.51 % | | 96 | 86 | | [Strpos()-like Comparison  ](http://exakat.readthedocs.io/en/latest/Rules.html#strpos-like-comparison) | 54.14 % | 7.46 % | | 97 | 105 | | [Return True False  ](http://exakat.readthedocs.io/en/latest/Rules.html#return-true-false) | 53.77 % | 12.67 % | | 98 | 104 | | [Undefined Class Constants  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-class-constants) | 53.71 % | 12.00 % | | 99 | 52 | | [Altering Foreach Without Reference  ](http://exakat.readthedocs.io/en/latest/Rules.html#altering-foreach-without-reference) | 52.80 % | -4.75 % | | 100 | 81 | | [Use === null  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-===-null) | 52.13 % | 3.94 % | | 101 | 177 | | [Forgotten Interface  ](http://exakat.readthedocs.io/en/latest/Rules.html#forgotten-interface) | 52.13 % | 27.81 % | | 102 | 118 | | [Global Usage  ](http://exakat.readthedocs.io/en/latest/Rules.html#global-usage) | 52.00 % | 15.78 % | | 103 | 288 | | [Callback Needs Return  ](http://exakat.readthedocs.io/en/latest/Rules.html#callback-needs-return) | 51.98 % | 47.96 % | | 104 | 113 | | [Timestamp Difference  ](http://exakat.readthedocs.io/en/latest/Rules.html#timestamp-difference) | 50.97 % | 13.65 % | | 105 | N/A | | [Incompatible Signature Methods  ](http://exakat.readthedocs.io/en/latest/Rules.html#incompatible-signature-methods) | 50.88 % | 50.88 % | | 106 | N/A | | [Method Signature Must Be Compatible  ](http://exakat.readthedocs.io/en/latest/Rules.html#method-signature-must-be-compatible) | 50.15 % | 50.15 % | | 107 | 34 | | [Constant Class  ](http://exakat.readthedocs.io/en/latest/Rules.html#constant-class) | 49.75 % | -15.95 % | | 108 | 126 | | [Same Conditions In Condition  ](http://exakat.readthedocs.io/en/latest/Rules.html#same-conditions-in-condition) | 49.75 % | 14.97 % | | 109 | 150 | | [Assign Default To Properties  ](http://exakat.readthedocs.io/en/latest/Rules.html#assign-default-to-properties) | 49.57 % | 20.88 % | | 110 | 108 | | [Repeated print()  ](http://exakat.readthedocs.io/en/latest/Rules.html#repeated-print) | 49.51 % | 9.47 % | | 111 | 142 | | [Unchecked Resources  ](http://exakat.readthedocs.io/en/latest/Rules.html#unchecked-resources) | 49.26 % | 18.58 % | | 112 | 119 | | [Useless Check  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-check) | 48.84 % | 12.62 % | | 113 | 130 | | [Could Be Else  ](http://exakat.readthedocs.io/en/latest/Rules.html#could-be-else) | 48.68 % | 14.45 % | | 114 | 128 | | [Unresolved Instanceof  ](http://exakat.readthedocs.io/en/latest/Rules.html#unresolved-instanceof) | 48.35 % | 13.84 % | | 115 | 99 | | [Should Typecast  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-typecast) | 47.92 % | 5.64 % | | 116 | 141 | | [Empty Try Catch  ](http://exakat.readthedocs.io/en/latest/Rules.html#empty-try-catch) | 47.80 % | 16.70 % | | 117 | 148 | | [Never Used Parameter  ](http://exakat.readthedocs.io/en/latest/Rules.html#never-used-parameter) | 47.71 % | 18.18 % | | 118 | 144 | | [Too Many Native Calls  ](http://exakat.readthedocs.io/en/latest/Rules.html#too-many-native-calls) | 47.47 % | 17.05 % | | 119 | 136 | | [Useless Catch  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-catch) | 47.34 % | 14.73 % | | 120 | 125 | | [Repeated Regex  ](http://exakat.readthedocs.io/en/latest/Rules.html#repeated-regex) | 47.13 % | 12.20 % | | 121 | N/A | | [strpos() Too Much  ](http://exakat.readthedocs.io/en/latest/Rules.html#strpos-too-much) | 46.86 % | 46.86 % | | 122 | 145 | | [Unconditional Break In Loop  ](http://exakat.readthedocs.io/en/latest/Rules.html#unconditional-break-in-loop) | 46.61 % | 16.40 % | | 123 | 157 | | [Avoid Using stdClass  ](http://exakat.readthedocs.io/en/latest/Rules.html#avoid-using-stdclass) | 45.55 % | 18.42 % | | 124 | 135 | | [Wrong Parameter Type  ](http://exakat.readthedocs.io/en/latest/Rules.html#wrong-parameter-type) | 45.43 % | 12.19 % | | 125 | 122 | | [If With Same Conditions  ](http://exakat.readthedocs.io/en/latest/Rules.html#if-with-same-conditions) | 44.82 % | 9.49 % | | 126 | 137 | | [Don't Change Incomings  ](http://exakat.readthedocs.io/en/latest/Rules.html#don) | 44.76 % | 12.30 % | | 127 | 165 | | [No Hardcoded Hash  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-hardcoded-hash) | 44.76 % | 18.58 % | | 128 | 139 | | [Should Make Alias  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-make-alias) | 44.15 % | 12.74 % | | 129 | 147 | | [Unthrown Exception  ](http://exakat.readthedocs.io/en/latest/Rules.html#unthrown-exception) | 43.90 % | 14.30 % | | 130 | N/A | | [Dont Mix ++  ](http://exakat.readthedocs.io/en/latest/Rules.html#dont-mix-++) | 43.81 % | 43.81 % | | 131 | 97 | | [Mixed Concat And Interpolation  ](http://exakat.readthedocs.io/en/latest/Rules.html#mixed-concat-and-interpolation) | 43.78 % | 0.37 % | | 132 | 146 | | [No Choice  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-choice) | 43.36 % | 13.20 % | | 133 | 143 | | [Multiple Type Variable  ](http://exakat.readthedocs.io/en/latest/Rules.html#multiple-type-variable) | 43.26 % | 12.74 % | | 134 | 79 | | [Empty Instructions  ](http://exakat.readthedocs.io/en/latest/Rules.html#empty-instructions) | 43.17 % | -5.65 % | | 135 | 107 | | [Class Should Be Final By Ocramius  ](http://exakat.readthedocs.io/en/latest/Rules.html#class-should-be-final-by-ocramius) | 42.87 % | 2.78 % | | 136 | 153 | | [Printf Number Of Arguments  ](http://exakat.readthedocs.io/en/latest/Rules.html#printf-number-of-arguments) | 42.77 % | 15.07 % | | 137 | 101 | | [Aliases Usage  ](http://exakat.readthedocs.io/en/latest/Rules.html#aliases-usage) | 42.69 % | 0.56 % | | 138 | 121 | | [Logical Should Use Symbolic Operators  ](http://exakat.readthedocs.io/en/latest/Rules.html#logical-should-use-symbolic-operators) | 42.50 % | 6.80 % | | 139 | 117 | | [Forgotten Visibility  ](http://exakat.readthedocs.io/en/latest/Rules.html#forgotten-visibility) | 42.38 % | 5.67 % | | 140 | 149 | | [Multiple Alias Definitions  ](http://exakat.readthedocs.io/en/latest/Rules.html#multiple-alias-definitions) | 42.26 % | 13.04 % | | 141 | N/A | | [Weak Typing  ](http://exakat.readthedocs.io/en/latest/Rules.html#weak-typing) | 41.56 % | 41.56 % | | 142 | 133 | | [No Return Used  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-return-used) | 41.35 % | 7.20 % | | 143 | 155 | | [Randomly Sorted Arrays  ](http://exakat.readthedocs.io/en/latest/Rules.html#randomly-sorted-arrays) | 41.23 % | 13.93 % | | 144 | 161 | | [No Direct Call To Magic Method  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-direct-call-to-magic-method) | 41.04 % | 14.53 % | | 145 | N/A | | [Possible Missing Subpattern  ](http://exakat.readthedocs.io/en/latest/Rules.html#possible-missing-subpattern) | 40.95 % | 40.95 % | | 146 | 138 | | [var_dump()... Usage  ](http://exakat.readthedocs.io/en/latest/Rules.html#var_dump...-usage) | 40.49 % | 8.24 % | | 147 | 199 | | [Don't Unset Properties  ](http://exakat.readthedocs.io/en/latest/Rules.html#don) | 40.28 % | 21.05 % | | 148 | 180 | | [Dangling Array References  ](http://exakat.readthedocs.io/en/latest/Rules.html#dangling-array-references) | 40.25 % | 16.00 % | | 149 | 152 | | [Only Variable Passed By Reference  ](http://exakat.readthedocs.io/en/latest/Rules.html#only-variable-passed-by-reference) | 40.07 % | 12.15 % | | 150 | 170 | | [Unused Returned Value  ](http://exakat.readthedocs.io/en/latest/Rules.html#unused-returned-value) | 39.70 % | 14.67 % | | 151 | 164 | | [Don't Send $this In Constructor  ](http://exakat.readthedocs.io/en/latest/Rules.html#don) | 39.24 % | 12.95 % | | 152 | 134 | | [Htmlentities Calls  ](http://exakat.readthedocs.io/en/latest/Rules.html#htmlentities-calls) | 39.22 % | 5.93 % | | 153 | 178 | | [Empty Interfaces  ](http://exakat.readthedocs.io/en/latest/Rules.html#empty-interfaces) | 38.79 % | 14.49 % | | 154 | 162 | | [Wrong Optional Parameter  ](http://exakat.readthedocs.io/en/latest/Rules.html#wrong-optional-parameter) | 38.42 % | 11.92 % | | 155 | 156 | | [Wrong Number Of Arguments  ](http://exakat.readthedocs.io/en/latest/Rules.html#wrong-number-of-arguments) | 38.42 % | 11.19 % | | 156 | N/A | | [Don't Read And Write In One Expression  ](http://exakat.readthedocs.io/en/latest/Rules.html#don) | 38.20 % | 38.20 % | | 157 | 175 | | [Useless Constructor  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-constructor) | 38.18 % | 13.72 % | | 158 | 168 | | [Static Methods Called From Object  ](http://exakat.readthedocs.io/en/latest/Rules.html#static-methods-called-from-object) | 38.12 % | 12.55 % | | 159 | 192 | | [Useless Casting  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-casting) | 37.75 % | 17.16 % | | 160 | 158 | | [Eval() Usage  ](http://exakat.readthedocs.io/en/latest/Rules.html#eval-usage) | 37.69 % | 10.88 % | | 161 | 166 | | [list() May Omit Variables  ](http://exakat.readthedocs.io/en/latest/Rules.html#list-may-omit-variables) | 37.45 % | 11.46 % | | 162 | 183 | | [No Direct Usage  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-direct-usage) | 36.90 % | 13.49 % | | 163 | 181 | | [Should Chain Exception  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-chain-exception) | 36.29 % | 12.35 % | | 164 | N/A | | [Undefined ::class  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-::class) | 34.79 % | 34.79 % | | 165 | 189 | | [Unset In Foreach  ](http://exakat.readthedocs.io/en/latest/Rules.html#unset-in-foreach) | 34.77 % | 13.29 % | | 166 | 229 | | [eval() Without Try  ](http://exakat.readthedocs.io/en/latest/Rules.html#eval-without-try) | 34.28 % | 20.80 % | | 167 | 188 | | [Undefined static:: Or self::  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-static::-or-self::) | 33.92 % | 12.18 % | | 168 | 154 | | [Make Global A Property  ](http://exakat.readthedocs.io/en/latest/Rules.html#make-global-a-property) | 33.86 % | 6.26 % | | 169 | 185 | | [No Hardcoded Path  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-hardcoded-path) | 33.61 % | 10.61 % | | 170 | 184 | | [Mismatched Default Arguments  ](http://exakat.readthedocs.io/en/latest/Rules.html#mismatched-default-arguments) | 33.43 % | 10.02 % | | 171 | 190 | | [Adding Zero  ](http://exakat.readthedocs.io/en/latest/Rules.html#adding-zero) | 33.25 % | 12.40 % | | 172 | 203 | | [Avoid get_class()  ](http://exakat.readthedocs.io/en/latest/Rules.html#avoid-get_class) | 32.76 % | 13.95 % | | 173 | 213 | | [Don't Echo Error  ](http://exakat.readthedocs.io/en/latest/Rules.html#don) | 32.70 % | 15.19 % | | 174 | 208 | | [Test Then Cast  ](http://exakat.readthedocs.io/en/latest/Rules.html#test-then-cast) | 32.48 % | 14.29 % | | 175 | 287 | | [Is Actually Zero  ](http://exakat.readthedocs.io/en/latest/Rules.html#is-actually-zero) | 32.35 % | 28.07 % | | 176 | 159 | | [Multiple Constant Definition  ](http://exakat.readthedocs.io/en/latest/Rules.html#multiple-constant-definition) | 32.09 % | 5.38 % | | 177 | 209 | | [Logical Mistakes  ](http://exakat.readthedocs.io/en/latest/Rules.html#logical-mistakes) | 31.24 % | 13.15 % | | 178 | 169 | | [Objects Don't Need References  ](http://exakat.readthedocs.io/en/latest/Rules.html#objects-don) | 31.18 % | 5.83 % | | 179 | 194 | | [Forgotten Thrown  ](http://exakat.readthedocs.io/en/latest/Rules.html#forgotten-thrown) | 30.99 % | 10.70 % | | 180 | 193 | | [Useless Switch  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-switch) | 30.57 % | 10.18 % | | 181 | 172 | | [Print And Die  ](http://exakat.readthedocs.io/en/latest/Rules.html#print-and-die) | 29.35 % | 4.63 % | | 182 | 187 | | [Useless Return  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-return) | 29.23 % | 7.23 % | | 183 | 174 | | [Implied If  ](http://exakat.readthedocs.io/en/latest/Rules.html#implied-if) | 29.23 % | 4.56 % | | 184 | 171 | | [One Variable String  ](http://exakat.readthedocs.io/en/latest/Rules.html#one-variable-string) | 29.11 % | 4.13 % | | 185 | 202 | | [No isset() With empty()  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-isset-with-empty) | 28.74 % | 9.72 % | | 186 | 214 | | [Useless Unset  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-unset) | 28.38 % | 10.92 % | | 187 | 238 | | [Redeclared PHP Functions  ](http://exakat.readthedocs.io/en/latest/Rules.html#redeclared-php-functions) | 28.25 % | 16.70 % | | 188 | N/A | | [Cant Instantiate Class  ](http://exakat.readthedocs.io/en/latest/Rules.html#cant-instantiate-class) | 28.21 % | 28.21 % | | 189 | 233 | | [Unpreprocessed Values  ](http://exakat.readthedocs.io/en/latest/Rules.html#unpreprocessed-values) | 28.19 % | 15.96 % | | 190 | 230 | | [Identical Conditions  ](http://exakat.readthedocs.io/en/latest/Rules.html#identical-conditions) | 28.13 % | 14.70 % | | 191 | 204 | | [Strange Name For Variables  ](http://exakat.readthedocs.io/en/latest/Rules.html#strange-name-for-variables) | 28.07 % | 9.46 % | | 192 | 206 | | [Redefined Default  ](http://exakat.readthedocs.io/en/latest/Rules.html#redefined-default) | 26.73 % | 8.23 % | | 193 | 216 | | [Non-constant Index In Array  ](http://exakat.readthedocs.io/en/latest/Rules.html#non-constant-index-in-array) | 26.24 % | 10.35 % | | 194 | 241 | | [Dependant Trait  ](http://exakat.readthedocs.io/en/latest/Rules.html#dependant-trait) | 26.24 % | 15.11 % | | 195 | 191 | | [Multiple Index Definition  ](http://exakat.readthedocs.io/en/latest/Rules.html#multiple-index-definition) | 25.21 % | 4.61 % | | 196 | 179 | | [Use Constant As Arguments  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-constant-as-arguments) | 25.15 % | 0.85 % | | 197 | N/A | | [Only Variable For Reference  ](http://exakat.readthedocs.io/en/latest/Rules.html#only-variable-for-reference) | 24.86 % | 24.86 % | | 198 | 186 | | [Useless Global  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-global) | 24.84 % | 2.41 % | | 199 | 220 | | [Foreach Reference Is Not Modified  ](http://exakat.readthedocs.io/en/latest/Rules.html#foreach-reference-is-not-modified) | 24.54 % | 9.54 % | | 200 | 223 | | [Should Use Constants  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-use-constants) | 24.48 % | 10.06 % | | 201 | N/A | | [Insufficient Typehint  ](http://exakat.readthedocs.io/en/latest/Rules.html#insufficient-typehint) | 24.43 % | 24.43 % | | 202 | 236 | | [Hidden Use Expression  ](http://exakat.readthedocs.io/en/latest/Rules.html#hidden-use-expression) | 24.29 % | 12.53 % | | 203 | 212 | | [No Hardcoded Ip  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-hardcoded-ip) | 24.17 % | 6.45 % | | 204 | 260 | | [Scalar Or Object Property  ](http://exakat.readthedocs.io/en/latest/Rules.html#scalar-or-object-property) | 24.11 % | 15.75 % | | 205 | 215 | | [Use PHP Object API  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-php-object-api) | 23.62 % | 6.68 % | | 206 | 217 | | [$this Belongs To Classes Or Traits  ](http://exakat.readthedocs.io/en/latest/Rules.html#$this-belongs-to-classes-or-traits) | 23.44 % | 8.18 % | | 207 | N/A | | [Continue Is For Loop  ](http://exakat.readthedocs.io/en/latest/Rules.html#continue-is-for-loop) | 23.21 % | 23.21 % | | 208 | 242 | | [Catch Overwrite Variable  ](http://exakat.readthedocs.io/en/latest/Rules.html#catch-overwrite-variable) | 23.14 % | 12.17 % | | 209 | 207 | | [Deprecated Functions  ](http://exakat.readthedocs.io/en/latest/Rules.html#deprecated-functions) | 22.95 % | 4.61 % | | 210 | 257 | | [No Real Comparison  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-real-comparison) | 22.28 % | 13.39 % | | 211 | 197 | | [Should Use Prepared Statement  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-use-prepared-statement) | 22.28 % | 2.68 % | | 212 | 195 | | [Var Keyword  ](http://exakat.readthedocs.io/en/latest/Rules.html#var-keyword) | 22.22 % | 2.36 % | | 213 | 231 | | [Must Return Methods  ](http://exakat.readthedocs.io/en/latest/Rules.html#must-return-methods) | 22.22 % | 9.37 % | | 214 | 132 | | [Unused Inherited Variable In Closure  ](http://exakat.readthedocs.io/en/latest/Rules.html#unused-inherited-variable-in-closure) | 22.18 % | -12.00 % | | 215 | 228 | | [Undefined Trait  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-trait) | 21.19 % | 7.39 % | | 216 | 205 | | [Could Be Static  ](http://exakat.readthedocs.io/en/latest/Rules.html#could-be-static) | 20.76 % | 2.21 % | | 217 | 218 | | [Non Static Methods Called In A Static  ](http://exakat.readthedocs.io/en/latest/Rules.html#non-static-methods-called-in-a-static) | 20.52 % | 5.36 % | | 218 | 243 | | [Nested Ternary  ](http://exakat.readthedocs.io/en/latest/Rules.html#nested-ternary) | 20.40 % | 9.48 % | | 219 | 201 | | [Alternative Syntax Consistence  ](http://exakat.readthedocs.io/en/latest/Rules.html#alternative-syntax-consistence) | 20.21 % | 1.13 % | | 220 | 232 | | [Illegal Name For Method  ](http://exakat.readthedocs.io/en/latest/Rules.html#illegal-name-for-method) | 19.73 % | 7.08 % | | 221 | 251 | | [Overwritten Exceptions  ](http://exakat.readthedocs.io/en/latest/Rules.html#overwritten-exceptions) | 19.67 % | 9.90 % | | 222 | 248 | | [Identical On Both Sides  ](http://exakat.readthedocs.io/en/latest/Rules.html#identical-on-both-sides) | 19.19 % | 8.69 % | | 223 | 221 | | [Results May Be Missing  ](http://exakat.readthedocs.io/en/latest/Rules.html#results-may-be-missing) | 18.81 % | 4.02 % | | 224 | 196 | | [Unused Global  ](http://exakat.readthedocs.io/en/latest/Rules.html#unused-global) | 18.75 % | -1.07 % | | 225 | 173 | | [Invalid Regex  ](http://exakat.readthedocs.io/en/latest/Rules.html#invalid-regex) | 18.52 % | -6.20 % | | 226 | 280 | | [Redefined Private Property  ](http://exakat.readthedocs.io/en/latest/Rules.html#redefined-private-property) | 18.22 % | 13.42 % | | 227 | 235 | | [Indices Are Int Or String  ](http://exakat.readthedocs.io/en/latest/Rules.html#indices-are-int-or-string) | 18.08 % | 5.96 % | | 228 | N/A | | [Assign And Compare  ](http://exakat.readthedocs.io/en/latest/Rules.html#assign-and-compare) | 17.42 % | 17.42 % | | 229 | 210 | | [Multiply By One  ](http://exakat.readthedocs.io/en/latest/Rules.html#multiply-by-one) | 17.05 % | -0.77 % | | 230 | 244 | | [Already Parents Interface  ](http://exakat.readthedocs.io/en/latest/Rules.html#already-parents-interface) | 17.05 % | 6.18 % | | 231 | 273 | | [Class, Interface Or Trait With Identical Names  ](http://exakat.readthedocs.io/en/latest/Rules.html#class,-interface-or-trait-with-identical-names) | 16.86 % | 11.17 % | | 232 | 253 | | [Wrong fopen() Mode  ](http://exakat.readthedocs.io/en/latest/Rules.html#wrong-fopen-mode) | 16.62 % | 6.95 % | | 233 | 222 | | [Use With Fully Qualified Name  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-with-fully-qualified-name) | 16.26 % | 1.84 % | | 234 | 240 | | [Strings With Strange Space  ](http://exakat.readthedocs.io/en/latest/Rules.html#strings-with-strange-space) | 15.83 % | 4.54 % | | 235 | 258 | | [Redefined Class Constants  ](http://exakat.readthedocs.io/en/latest/Rules.html#redefined-class-constants) | 15.71 % | 6.83 % | | 236 | 227 | | [While(List() = Each())  ](http://exakat.readthedocs.io/en/latest/Rules.html#whilelist-=-each) | 15.46 % | 1.66 % | | 237 | 225 | | [Forgotten Whitespace  ](http://exakat.readthedocs.io/en/latest/Rules.html#forgotten-whitespace) | 15.34 % | 1.13 % | | 238 | 255 | | [Unknown Pcre2 Option  ](http://exakat.readthedocs.io/en/latest/Rules.html#unknown-pcre2-option) | 15.29 % | 6.04 % | | 239 | 237 | | [Suspicious Comparison  ](http://exakat.readthedocs.io/en/latest/Rules.html#suspicious-comparison) | 15.10 % | 3.34 % | | 240 | 226 | | [Old Style Constructor  ](http://exakat.readthedocs.io/en/latest/Rules.html#old-style-constructor) | 14.92 % | 1.01 % | | 241 | 269 | | [Missing Parenthesis  ](http://exakat.readthedocs.io/en/latest/Rules.html#missing-parenthesis) | 14.50 % | 8.39 % | | 242 | 246 | | [Lone Blocks  ](http://exakat.readthedocs.io/en/latest/Rules.html#lone-blocks) | 14.31 % | 3.81 % | | 243 | 247 | | [Useless Brackets  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-brackets) | 14.31 % | 3.81 % | | 244 | 286 | | [Lost References  ](http://exakat.readthedocs.io/en/latest/Rules.html#lost-references) | 14.19 % | 9.86 % | | 245 | 301 | | [Accessing Private  ](http://exakat.readthedocs.io/en/latest/Rules.html#accessing-private) | 13.52 % | 11.70 % | | 246 | 245 | | [Or Die  ](http://exakat.readthedocs.io/en/latest/Rules.html#or-die) | 13.33 % | 2.61 % | | 247 | 277 | | [__DIR__ Then Slash  ](http://exakat.readthedocs.io/en/latest/Rules.html#__dir__-then-slash) | 13.27 % | 7.94 % | | 248 | N/A | | [Can't Throw Throwable  ](http://exakat.readthedocs.io/en/latest/Rules.html#can) | 13.16 % | 13.16 % | | 249 | 259 | | [Not Not  ](http://exakat.readthedocs.io/en/latest/Rules.html#not-not) | 13.15 % | 4.79 % | | 250 | 263 | | [Same Variables Foreach  ](http://exakat.readthedocs.io/en/latest/Rules.html#same-variables-foreach) | 12.91 % | 6.07 % | | 251 | 224 | | [Could Use str_repeat()  ](http://exakat.readthedocs.io/en/latest/Rules.html#could-use-str_repeat) | 12.85 % | -1.37 % | | 252 | 252 | | [Deep Definitions  ](http://exakat.readthedocs.io/en/latest/Rules.html#deep-definitions) | 12.36 % | 2.69 % | | 253 | N/A | | [Typehinted References  ](http://exakat.readthedocs.io/en/latest/Rules.html#typehinted-references) | 12.30 % | 12.30 % | | 254 | 290 | | [Empty Traits  ](http://exakat.readthedocs.io/en/latest/Rules.html#empty-traits) | 12.24 % | 8.53 % | | 255 | N/A | | [Mismatch Type And Default  ](http://exakat.readthedocs.io/en/latest/Rules.html#mismatch-type-and-default) | 11.82 % | 11.82 % | | 256 | 264 | | [No Hardcoded Port  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-hardcoded-port) | 11.51 % | 4.77 % | | 257 | 262 | | [Multiples Identical Case  ](http://exakat.readthedocs.io/en/latest/Rules.html#multiples-identical-case) | 10.96 % | 3.12 % | | 258 | 256 | | [Avoid Parenthesis  ](http://exakat.readthedocs.io/en/latest/Rules.html#avoid-parenthesis) | 10.53 % | 1.39 % | | 259 | 283 | | [No Magic With Array  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-magic-with-array) | 10.41 % | 5.71 % | | 260 | N/A | | [Typehint Must Be Returned  ](http://exakat.readthedocs.io/en/latest/Rules.html#typehint-must-be-returned) | 10.05 % | 10.05 % | | 261 | 281 | | [Ambiguous Array Index  ](http://exakat.readthedocs.io/en/latest/Rules.html#ambiguous-array-index) | 9.86 % | 5.16 % | | 262 | 282 | | [Instantiating Abstract Class  ](http://exakat.readthedocs.io/en/latest/Rules.html#instantiating-abstract-class) | 9.56 % | 4.86 % | | 263 | 293 | | [Wrong Range Check  ](http://exakat.readthedocs.io/en/latest/Rules.html#wrong-range-check) | 9.26 % | 6.49 % | | 264 | 298 | | [Implement Is For Interface  ](http://exakat.readthedocs.io/en/latest/Rules.html#implement-is-for-interface) | 9.01 % | 6.87 % | | 265 | 261 | | [preg_replace With Option e  ](http://exakat.readthedocs.io/en/latest/Rules.html#preg_replace-with-option-e) | 8.83 % | 0.68 % | | 266 | 234 | | [Assign With And  ](http://exakat.readthedocs.io/en/latest/Rules.html#assign-with-and) | 8.70 % | -3.47 % | | 267 | 294 | | [Multiple Alias Definitions Per File  ](http://exakat.readthedocs.io/en/latest/Rules.html#multiple-alias-definitions-per-file) | 8.46 % | 5.80 % | | 268 | 265 | | [Use Pathinfo  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-pathinfo) | 8.09 % | 1.51 % | | 269 | 274 | | [Next Month Trap  ](http://exakat.readthedocs.io/en/latest/Rules.html#next-month-trap) | 7.67 % | 2.08 % | | 270 | 316 | | [Inclusion Wrong Case  ](http://exakat.readthedocs.io/en/latest/Rules.html#inclusion-wrong-case) | 7.37 % | 6.49 % | | 271 | N/A | | [Undefined Insteadof  ](http://exakat.readthedocs.io/en/latest/Rules.html#undefined-insteadof) | 7.37 % | 7.37 % | | 272 | 266 | | [Queries In Loops  ](http://exakat.readthedocs.io/en/latest/Rules.html#queries-in-loops) | 7.12 % | 0.70 % | | 273 | 296 | | [Strtr Arguments  ](http://exakat.readthedocs.io/en/latest/Rules.html#strtr-arguments) | 6.82 % | 4.42 % | | 274 | 276 | | [Failed Substr Comparison  ](http://exakat.readthedocs.io/en/latest/Rules.html#failed-substr-comparison) | 6.76 % | 1.28 % | | 275 | 313 | | [Should Use SetCookie()  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-use-setcookie) | 6.69 % | 5.70 % | | 276 | 254 | | [Access Protected Structures  ](http://exakat.readthedocs.io/en/latest/Rules.html#access-protected-structures) | 6.51 % | -3.05 % | | 277 | 285 | | [Too Many Finds  ](http://exakat.readthedocs.io/en/latest/Rules.html#too-many-finds) | 6.45 % | 1.90 % | | 278 | 324 | | [Throws An Assignement  ](http://exakat.readthedocs.io/en/latest/Rules.html#throws-an-assignement) | 5.84 % | 5.48 % | | 279 | 278 | | [Static Methods Can't Contain $this  ](http://exakat.readthedocs.io/en/latest/Rules.html#static-methods-can) | 5.54 % | 0.42 % | | 280 | 279 | | [$this Is Not For Static Methods  ](http://exakat.readthedocs.io/en/latest/Rules.html#$this-is-not-for-static-methods) | 5.54 % | 0.42 % | | 281 | 275 | | [Missing New ?  ](http://exakat.readthedocs.io/en/latest/Rules.html#missing-new-?) | 5.42 % | -0.17 % | | 282 | 289 | | [Multiple Class Declarations  ](http://exakat.readthedocs.io/en/latest/Rules.html#multiple-class-declarations) | 5.35 % | 1.49 % | | 283 | 271 | | [Mismatched Typehint  ](http://exakat.readthedocs.io/en/latest/Rules.html#mismatched-typehint) | 5.17 % | -0.73 % | | 284 | 284 | | [Old Style __autoload()  ](http://exakat.readthedocs.io/en/latest/Rules.html#old-style-__autoload) | 4.93 % | 0.28 % | | 285 | N/A | | [Abstract Or Implements  ](http://exakat.readthedocs.io/en/latest/Rules.html#abstract-or-implements) | 4.14 % | 4.14 % | | 286 | 320 | | [__toString() Throws Exception  ](http://exakat.readthedocs.io/en/latest/Rules.html#__tostring-throws-exception) | 3.95 % | 3.28 % | | 287 | 291 | | [Throw Functioncall  ](http://exakat.readthedocs.io/en/latest/Rules.html#throw-functioncall) | 3.77 % | 0.59 % | | 288 | N/A | | [Wrong Access Style to Property  ](http://exakat.readthedocs.io/en/latest/Rules.html#wrong-access-style-to-property) | 3.71 % | 3.71 % | | 289 | 239 | | [self, parent, static Outside Class  ](http://exakat.readthedocs.io/en/latest/Rules.html#self,-parent,-static-outside-class) | 3.59 % | -7.75 % | | 290 | 295 | | [Crc32() Might Be Negative  ](http://exakat.readthedocs.io/en/latest/Rules.html#crc32-might-be-negative) | 3.59 % | 0.93 % | | 291 | 309 | | [Empty Namespace  ](http://exakat.readthedocs.io/en/latest/Rules.html#empty-namespace) | 3.53 % | 2.23 % | | 292 | 270 | | [$this Is Not An Array  ](http://exakat.readthedocs.io/en/latest/Rules.html#$this-is-not-an-array) | 3.47 % | -2.43 % | | 293 | 300 | | [Ternary In Concat  ](http://exakat.readthedocs.io/en/latest/Rules.html#ternary-in-concat) | 3.41 % | 1.48 % | | 294 | 304 | | [Silently Cast Integer  ](http://exakat.readthedocs.io/en/latest/Rules.html#silently-cast-integer) | 3.34 % | 1.72 % | | 295 | 297 | | [Only Variable Returned By Reference  ](http://exakat.readthedocs.io/en/latest/Rules.html#only-variable-returned-by-reference) | 3.16 % | 0.97 % | | 296 | N/A | | [Bad Constants Names  ](http://exakat.readthedocs.io/en/latest/Rules.html#bad-constants-names) | 2.86 % | 2.86 % | | 297 | 299 | | [Missing Cases In Switch  ](http://exakat.readthedocs.io/en/latest/Rules.html#missing-cases-in-switch) | 2.74 % | 0.60 % | | 298 | 331 | | [Can't Extend Final  ](http://exakat.readthedocs.io/en/latest/Rules.html#can) | 2.49 % | 2.34 % | | 299 | N/A | | [Method Collision Traits  ](http://exakat.readthedocs.io/en/latest/Rules.html#method-collision-traits) | 2.49 % | 2.49 % | | 300 | 305 | | [Parent, Static Or Self Outside Class  ](http://exakat.readthedocs.io/en/latest/Rules.html#parent,-static-or-self-outside-class) | 2.00 % | 0.49 % | | 301 | 302 | | [error_reporting() With Integers  ](http://exakat.readthedocs.io/en/latest/Rules.html#error_reporting-with-integers) | 1.70 % | -0.07 % | | 302 | 311 | | [Useless Final  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-final) | 1.70 % | 0.55 % | | 303 | 312 | | [Constants With Strange Names  ](http://exakat.readthedocs.io/en/latest/Rules.html#constants-with-strange-names) | 1.40 % | 0.41 % | | 304 | 303 | | [Use System Tmp  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-system-tmp) | 1.33 % | -0.44 % | | 305 | 315 | | [No Empty Regex  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-empty-regex) | 1.33 % | 0.45 % | | 306 | 314 | | [Invalid Constant Name  ](http://exakat.readthedocs.io/en/latest/Rules.html#invalid-constant-name) | 1.03 % | 0.15 % | | 307 | 318 | | [Always Positive Comparison  ](http://exakat.readthedocs.io/en/latest/Rules.html#always-positive-comparison) | 0.97 % | 0.24 % | | 308 | 325 | | [No Reference For Ternary  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-reference-for-ternary) | 0.79 % | 0.43 % | | 309 | 267 | | [Unkown Regex Options  ](http://exakat.readthedocs.io/en/latest/Rules.html#unkown-regex-options) | 0.73 % | -5.49 % | | 310 | 329 | | [Constants Created Outside Its Namespace  ](http://exakat.readthedocs.io/en/latest/Rules.html#constants-created-outside-its-namespace) | 0.60 % | 0.45 % | | 311 | 317 | | [Throw In Destruct  ](http://exakat.readthedocs.io/en/latest/Rules.html#throw-in-destruct) | 0.54 % | -0.24 % | | 312 | 327 | | [No Reference On Left Side  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-reference-on-left-side) | 0.42 % | 0.11 % | | 313 | 344 | | [Possible Infinite Loop  ](http://exakat.readthedocs.io/en/latest/Rules.html#possible-infinite-loop) | 0.42 % | 0.42 % | | 314 | N/A | | [Clone With Non-Object  ](http://exakat.readthedocs.io/en/latest/Rules.html#clone-with-non-object) | 0.42 % | 0.42 % | | 315 | 326 | | [Compared Comparison  ](http://exakat.readthedocs.io/en/latest/Rules.html#compared-comparison) | 0.30 % | -0.01 % | | 316 | 333 | | [Pathinfo() Returns May Vary  ](http://exakat.readthedocs.io/en/latest/Rules.html#pathinfo-returns-may-vary) | 0.24 % | 0.14 % | | 317 | 341 | | [No get_class() With Null  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-get_class-with-null) | 0.24 % | 0.19 % | | 318 | 334 | | [Foreach On Object  ](http://exakat.readthedocs.io/en/latest/Rules.html#foreach-on-object) | 0.24 % | 0.14 % | | 319 | N/A | | [Invalid Pack Format  ](http://exakat.readthedocs.io/en/latest/Rules.html#invalid-pack-format) | 0.24 % | 0.24 % | | 320 | N/A | | [Should Yield With Key  ](http://exakat.readthedocs.io/en/latest/Rules.html#should-yield-with-key) | 0.18 % | 0.18 % | | 321 | 330 | | [Hash Algorithms  ](http://exakat.readthedocs.io/en/latest/Rules.html#hash-algorithms) | 0.12 % | -0.03 % | | 322 | 321 | | [Multiple Identical Trait Or Interface  ](http://exakat.readthedocs.io/en/latest/Rules.html#multiple-identical-trait-or-interface) | 0.06 % | -0.61 % | | 323 | 339 | | [Strange Name For Constants  ](http://exakat.readthedocs.io/en/latest/Rules.html#strange-name-for-constants) | 0.06 % | 0.01 % | | 324 | N/A | | [Must Call Parent Constructor  ](http://exakat.readthedocs.io/en/latest/Rules.html#must-call-parent-constructor) | 0.06 % | 0.06 % | | 325 | N/A | | [Repeated Interface  ](http://exakat.readthedocs.io/en/latest/Rules.html#repeated-interface) | 0.06 % | 0.06 % | | 326 | N/A | | [Useless Alias  ](http://exakat.readthedocs.io/en/latest/Rules.html#useless-alias) | 0.06 % | 0.06 % | | 327 | N/A | | [Methods Without Return  ](http://exakat.readthedocs.io/en/latest/Rules.html#methods-without-return) | 0.00 % | 0.00 % | | 328 | 182 | | [Incompilable Files  ](http://exakat.readthedocs.io/en/latest/Rules.html#incompilable-files) | 0.00 % | -23.41 % | | 329 | 323 | | [Abstract Static Methods  ](http://exakat.readthedocs.io/en/latest/Rules.html#abstract-static-methods) | 0.00 % | -0.57 % | | 330 | 342 | | [Short Open Tags  ](http://exakat.readthedocs.io/en/latest/Rules.html#short-open-tags) | 0.00 % | 0.00 % | | 331 | 332 | | [Fully Qualified Constants  ](http://exakat.readthedocs.io/en/latest/Rules.html#fully-qualified-constants) | 0.00 % | -0.10 % | | 332 | N/A | | [Use Constant  ](http://exakat.readthedocs.io/en/latest/Rules.html#use-constant) | 0.00 % | 0.00 % | | 333 | 335 | | [Concrete Visibility  ](http://exakat.readthedocs.io/en/latest/Rules.html#concrete-visibility) | 0.00 % | -0.05 % | | 334 | 272 | | [No Self Referencing Constant  ](http://exakat.readthedocs.io/en/latest/Rules.html#no-self-referencing-constant) | 0.00 % | -5.75 % | | 335 | 310 | | [Break Outside Loop  ](http://exakat.readthedocs.io/en/latest/Rules.html#break-outside-loop) | 0.00 % | -1.25 % | | 336 | 336 | | [Empty List  ](http://exakat.readthedocs.io/en/latest/Rules.html#empty-list) | 0.00 % | -0.05 % | | 337 | 337 | | [func_get_arg() Modified  ](http://exakat.readthedocs.io/en/latest/Rules.html#func_get_arg-modified) | 0.00 % | -0.05 % | | 338 | 338 | | [Negative Power  ](http://exakat.readthedocs.io/en/latest/Rules.html#negative-power) | 0.00 % | -0.05 % | | 339 | 343 | | [Using $this Outside A Class  ](http://exakat.readthedocs.io/en/latest/Rules.html#using-$this-outside-a-class) | 0.00 % | 0.00 % | | 340 | 340 | | [Implemented Methods Are Public  ](http://exakat.readthedocs.io/en/latest/Rules.html#implemented-methods-are-public) | 0.00 % | -0.05 % | | 341 | 268 | | [Too Many Injections  ](http://exakat.readthedocs.io/en/latest/Rules.html#too-many-injections) | 0.00 % | -6.17 % | | 342 | N/A | | [Assert Function Is Reserved  ](http://exakat.readthedocs.io/en/latest/Rules.html#assert-function-is-reserved) | 0.00 % | 0.00 % | ## EPIC Methodology The "Exakat PHP Index of Coding", aka EPIC, represents how often an static analysis rule reports results when auditing PHP code. The higher the rating, the higher is the probability to report issues. The lower the rating, the rarer are the issues. This popularity is built by analyzing 1730 Open Source project, with PHP 7.1. Any issue reported by Exakat makes the project count as affected. Only when a project reports no issues, is it counted as error free.   ## EPIC FAQ - **Can I reuse those results in an article or in my code? **Yes. Simply mention 'https://www.exakat.io' as the source, and may be the month of publication (Current is 06/2020 - **Is there a computer-readable version ? ** The Exakat PHP Index of Coding is [available as JSON ](https://www.exakat.io/epic/month.2020-06.json). - **How does the index handle the false-positives ?** False positives are only human-detectable. Help us reduce the false positive by reporting bugs and informations to remove them. - **Why are some rules down to 0 ? Aren't they useless?** Some analysis require an old version of PHP, while the Index works with more recent versions of PHP (7.1 at the moment). As such, those analysis will dwindle to the bottom of the ranking and disappear.   --- # Removing Unused Variables Is Good Source: https://www.exakat.io/removing-unused-variables-is-good/ [![](https://www.exakat.io/wp-content/uploads/2020/09/stamp.320-250x300.jpg)](https://www.exakat.io/wp-content/uploads/2020/09/stamp.320.jpg) # Removing Unused Variables Is Good My favorite code clean is removing unused variables. This is right : variables that are hosted in the code, yet doing nothing. In fact, they are often toxic and grow bug-prone. Any old enough code source has them, and they tend to grow by the number. Have you ever tried spotting and removing those variables? Well, removing unused variables is my world and welcome to it! ## What Is An Unused Variable The simplest form and definition of unused variables are variables that are assigned, and never used. We can start with an example like this one: The `$unused` variable is created, with the value `1`; Then, the script ends. On the other hand, the value `2` is assigned to the variable `$used`, then used for display. It is not much, but it is honest work. As usual, with code, this illustration has variations. In particular, variables are dependent on a context, so the end of the script is not necessary : the end of a method is sufficient. Also, note how `$arg`, being a reference, is used once, but not unused : we need to check the calling context too. This would apply to global variables and properties. The second variation style is when the variable is prepared. This is the case when it is augmented or formatted, before usage. That way, the variable is created, then used to update itself. Such a variable is unused if it is not used in the end. This may cover situations like the following : The last example is the most complex of the three : the variable is created, augmented with a function call including itself and other values, and then, never used. The nature of the function called is important here : array_merge has no side effect, so this last expression doesn't use the `$unusedToo` variable, even as it merges it with `$b`. It could have been very different if `array_merge` was replaced by a methodcall, that stored the value for later. ## Finding Unused Variables With Static analysis Finding unused variable might be quite laborious, and easily automatised. So, our friends the static analysis tools will find those pesky variables for us, in no time. We'll use Exakat to run the errands for us. Exakat offers analysis rules for unused variables : [Variables/UsedOncePerContext](https://exakat.readthedocs.io/en/latest/Rules.html#used-once-variables-in-scope) and [Variables/WrittenOnlyVariables](https://exakat.readthedocs.io/en/latest/Rules.html#used-once-variables). We can also take a look at [Classes/UsedOnceProperty](https://exakat.readthedocs.io/en/latest/Rules.html#used-once-property), which extends the concept to class properties. ## Running the audit After the [installation](https://exakat.readthedocs.io/en/latest/Installation.html) process, the simplest way to get the above mentioned results is to use the [Diplomat](https://exakat.readthedocs.io/en/latest/Reports.html#diplomat) report. It is the default run, so you can use the following commands in your terminal : ``` cd /some/folder/with/exakat php exakat.phar init -p robo -R https://github.com/consolidation/Robo -v php exakat.phar project -v -p robo ``` The result is available in HTML form, in `projects\robo\report\index.html`. If you prefer a text version of the results, you can read the [Diplomat](https://exakat.readthedocs.io/en/latest/Reports.html#text) report : ``` cd /some/folder/with/exakat php exakat.phar report -p robo --format Text -P Variables/VariableUsedOnceByContext -O Variables/WrittenOnlyVariable -P Classes/UsedOnceProperty ``` You will get a text result, similar to this one : /src/Task/Archive/Extract.php:175 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $status /src/Task/Assets/ImageMinify.php:192 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $url /src/Task/Assets/ImageMinify.php:131 Classes/UsedOnceProperty Used Once Property $minifiers = ['optipng', 'gifsicle', 'jpegtran', 'svgo', 'pngquant', 'advpng', 'pngout', 'zopflipng', 'pngcrush', 'jpegoptim', 'jpeg-recompress', ] /RoboFile.php:25 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $taskPHPUnit /RoboFile.php:316 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $m /RoboFile.php:312 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $m /src/Runner.php:188 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $container /src/Runner.php:291 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $color /src/Runner.php:498 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $argv /src/Runner.php:149 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $argv /src/Log/ResultPrinter.php:98 Variables/VariableUsedOnceByContext Used Once Variables (In Scope) $task ## Reviewing robo.li We'll use the `Robo.li`, a smart task runner for PHP in PHP. The code is available at github, as you can see in the `init`command. It is also available on [packagist.org](https://packagist.org/packages/consolidation/robo). It's a very useful tool for automating development, and we like it very much. This is also why we're using it in this article. ## Removing unused variables We'll start with the 'Used once variables'. Those are variable that are used only once in the entire application. All in all, there is only one mention of the variable. This is a good candidate to be an unused variable, but we need to check if it is worth removing directly. The first we can visit is Robofile.php:25 We can see `$taskPHPUnit` being assigned with the result of the task `tastPHPunit()`, but never used again. `tastPHPunit()` is the method that actually runs the tests, so, the method is important. Since the result is not use later in the method, there is no need to keep it. The next variable, `$taskCodecept`, on the other hand, is collected then used twice. Congratulations! We just spotted our first unused variable. Let's see a few of other situations. ## Iffectations The second one we'll check is `$status` in the file `src/Task/Archive/Extract.php:175`. The code is below. It is a classic example of an 'iffectation' : an affectation in a if() condition. Here, `$status` is collected as the result of the condition, and not used later. The assignation may be removed, while keeping the !== comparison. ## Blind variables For the third one, we'll switch to the 'Used once variable (in scope)' rule. You can select it in the Analysis column of the audit. We are looking at the file `src/Collection/Collection.php:806`. It is the following code, about variable `$name`. It is in the foreach() loop. `$name` is the key in the foreach loop. Only the value `$taskGroup` is used in the loop, which makes `$name` collected for nothing. It may be dropped, with a tiny improvement in performances. This is definitely a micro-optimisation, so you may assume it now worth your time. Interestingly, there is another unused variable in another loop, in file `src/Runner.php:291`. Here, we are looking at variable `$color` (most of the code of the method has been omitted here. Just trust me). This is actually the opposite of the situation we just reviewed : the value is unused, while the key is used. This time, it is not possible to drop the value, as it is required in the loop. Using array_keys() on `$this->errorConditions` is possible, and make the intention very clear. However, the micro-optimisation we mentioned above will be void. So, at that point, the choice is yours. ## Caught Exceptions Next, we check `src/Task/Base/ParallelExec.php:175`, for variable `$e`. `$e` is often used for exceptions in a catch clause, and this is exactly the case here. In the catch clause, `$e` is not mentioned. The exception is caught and processed. Thanks to its actual type, the logging doesn't have to rely. Maybe this code will benefit from the [Anonymous catches](https://wiki.php.net/rfc/anonymous_catch) that PHP 8.0 will feature. Until then, this is actually an unused variable and a false positive that we can ignore. ## Used once properties Let's round this up with a check on a property or two. Check the file `src/Task/Development/SemVer.php:37`, for the property `protected $specialSeparator`. It is a protected property, so we need to check it with its parents. Since the class is not `abstract`, there is no need to check with potential child classes : `SemVer` is an autonomous class, and might be used just like that. Here, `class SemVer implements TaskInterface` doesn't extend any other class. The `protected` is actually a `private` property (since no other class can access it, except itself). The only usage of the property is in the method above. The property is actually updated with a new separator. The method itself is never used in the rest of the robo code, so we might be looking at a dead method at the same time. That would require a different audit. The most important part is that `specialSeparator` is assigned, but never used. It is indeed a candidate for removal, along with its method. ## Removing Variables Is Always Interesting Out of the 6 examples that we reviewed, we spotted different kind of variables - one unused return value - one unused iffectation - one property - one caught exception - two blind variables and 4 of them are good candidates for removal. This is a meager harvest, over a code that is obviously reviewed by itself. Usually, unused variables are in greater numbers than those, and they might also hold results of significant computation. Ideally, we'd like to spot those to remove the variable and its calculations, earning a nice bump in speed. When you want to try the same review on your own code, use the auditing command that we applied on this repository, using your repository during initialisation. There are hundreds of analysis rules to checks! Happy PHP auditing with [Exakat](https://www.exakat.io/)! --- # Typehint Recommendations for All Source: https://www.exakat.io/typehint-recommendations-for-all/ # Typehint Recommendations for All The process of adding strong typing to a PHP code base is a long process. A standard application will require on average 11 000 arguments and return type to add, and that is not counting the properties (everyone is on PHP 7.4, right?) The vast majority of typehints are already in the code. One may deduce them from reading the source, and checking for usage and prerequisites. And that is a work for static analysis tool. ## Typehint suggestions Here is an example of the results from the 'typehint suggestion' report of Exakat. Functions, closures, arrow functions and methods (traits and class) are presented in a table. # Standalone functions are sorted first, while methods are displayed after. ### functions Functions are presented first. They are referenced by their name. Parameters and return type are displayed in the third column. When a typehint is present in the code, it is displayed. Otherwise, Exakat displays one or several type suggestion. ### closures Closures are presented next. The format is the same as for functions, although the file and line are used to distinguish the closures. The names of the parameters may also help to distinguish them. Arrow functions are displayed like closures. ### Classes, interfaces and traits All of them are presented with their class name, and their namespace below. Properties are presented first, or simply omitted when there is none. Each method gets a green tick to mention that all parameters and the return type have been specified in the code : so far, so good. Classes also get a tick, when all methods also have a tick, or when all properties have a tick. Also, when all properties and methods have the typehints. ## Suggestions The last column is the suggestion column. It contains possible types that Exakat could find. You'll find the scalar types, and also `iterable`. Nullable typehint, which is represented with `?` in PHP 7.x, is represented with `null` in the report. The `null` notation will be the available in PHP 8.0 code, and it makes the report easier to read. The PHP 8.0 type `stringeable` is not supported yet. `Class, Interface` type is a generic suggestion to cover any object. It is possible to use the `object` scalar type in the code, although it won't help much. Currently, no further detail is provided on which class or interface is possible. Although it is not always possible to detect it, details on possible classes will be available in a future release of Exakat. It is also possible that no suggestion is provided. This might be a legit situation, like when a property acts as a cache for any kind of data; or because the method could not be found in the code; or if suggestions depends on typehints that are not yet coded (a.k.a, chained suggestions). And if we missed a case, [drop us a note](https://twitter.com/exakat). ## Inking the Typehints With the suggestions at hand, it is simply a matter of choosing the right one to use. Several situations are possible and require different treatments. One type is suggested. Check for yourself, and just use it. If the second type is `null` : then, use the nullable type with `?`. Multiple types are suggested. Then, you may have yourself the following questions : + Does the method actually handle multiple types? For example, with is_array() or instanceof ? It will probably need those multiple types. Stay in PHP 7.x and do not add any type; move to PHP 8.0 and use union types. + Does the method should use one of those types? Then use the type, and be ready to fix some types here and there. This is a situation where compelling a parameter to be `int` will suddenly raise red flags for any part of the code sending a `string` or a `null`. Exakat checks the actual usage of the method, and if `string` are used, it will suggest that type too. Last piece of advice : don't do too many of them at once. Add them by small batch, and see the impact on your code, and on the audit itself. ## Running the audit on your code Are you ready to get your own suggestions? Let's go! - Install Exakat - Run the audit - Update your code ### Install Exakat [Installing Exakat](https://exakat.readthedocs.io/en/latest/Installation.html#quick-installation-with-exakat-phar) comes in various flavors, including [Docker](https://exakat.readthedocs.io/en/latest/Installation.html#installation-guide-with-docker) or commandline. We'll do the commandline version. You need PHP 7.4, or 7.3 to run Exakat. You need `Java 8`, `unzip` already installed on the target machine. That should be a low bar to pass. Let's do the curl installation quickly. Copy and paste the following script : ``` mkdir exakat cd exakat curl -o exakat.phar 'https://www.exakat.io/versions/index.php?file=latest' php exakat.phar install # Optional health check php exakat.phar doctor ``` The `install` command download a copy of the `tinkergraph` database, and install it in the same folder as Exakat. The `doctor` command will display the current installation. ### Run the audit Let's run a quick audit on an open source project so we can both check the installation and get our first results. First, we initialize the project itself, run the audit, and extract the typehint report : ``` cd exakat php exakat.phar init -p sculpin -R https://github.com/sculpin/sculpin.git php exakat.phar project -p sculpin -v php exakat.phar report -p sculpin -format Typesuggestion ``` In the end, the report is in the file `projects/sculpin/typehints.suggestion.html`, that you can open with your favorite browser. ### Update your code If you have replaced `sculpin` with your own code, it is time to review the list of suggestions and adopt the one that makes sense in your code. Happy auditing! ## Speed up the adoption of the strong types Exakat takes the laborious work of checking your code from your arms, so you can spend more time on what matters the most : delivering great applications. And this is just the beginning : with the audit above, you can generate several [other reports](https://exakat.readthedocs.io/en/latest/Reports.html), including security, performances, PHP 8 compatibility and others. --- # Exakat 2.1.8 review Source: https://www.exakat.io/exakat-2-1-8-review/ # Exakat 2.1.8 Review Exakat 2.1.8 shines with the last warmth of summer. It is now time to start the resolute road to PHP 8.0 and its surprise incompatibility; or update PHP code stubs with the upcoming attributes. During the trip, we'll stop observing the new SARIF format, the standard for static analysis tool in PHP and everywhere else. Enjoy the Exakat 2.1.8 Review. ## Attributes support in Exakat stubs Attributes are coming to PHP 8.0. They are a way to add metadata to coding structures, such as classes, interfaces, traits or functions. They will replace structured PHPdoc, that we grew familiar with. PHP 8.0 attributes look like this, since the [final vote](https://wiki.php.net/rfc/shorter_attribute_syntax_change) on the `#[Attribute]` syntax. ``` ``` ``` ``` We'll see progressive adoption of the attributes, as tools will adapt to this new way to convey configurations in the code. IDE are part of those tools, and we'll need new stubs to documents accurately the frameworks. Exakat provides two ways to create stubs. [Stubs](https://exakat.readthedocs.io/en/latest/Reports.html#stubs) and [StubsJson](https://exakat.readthedocs.io/en/latest/Reports.html#stubsjson) both extract all information concerning a code source, and builds a `stubs.php` file with all needed PHP code, or a `stubs.json` file, with the same information, stored in JSON format. Indeed, internally, Exakat uses the `Json` version to build the `PHP` version. Json will also be used soon to provide stubs during Exakat audits, with better performances and more flexibility. We shall detail that point in an upcoming version. Some of the work in progress is already available on [exakat/stubs](https://github.com/exakat/stubs). ## New report : SARIF Exakat 2.1.8 offers a new report, called SARIF. [Static Analysis Results Interchange Format (SARIF) Version 2.0](https://docs.oasis-open.org/sarif/sarif/v2.0/sarif-v2.0.html) is a standard for static analysis results. Based on JSON, it provides a way to describe issues that were detected during the audit. The precise location (file and line number), but also documentation, description, severity level and unique identifier. This standard is a machine-oriented standards, which aims at reducing the gap between the formats used by every SCA. This is true in the PHP world, where [each tool](https://github.com/exakat/php-static-analysis-tools) like Psalm, Phan, noverify, etc. return different format. Exakat alone provides more than 30 different formats, including human-readable formats. Using SARIF format means that a generation of visualisation tools will appear : the SARIF viewers. Now that issues have been standardised, it is possible to collect multiple sources, display them in one GUI, and provide complementary analysis by leveraging multiple points of view on the code. Anyone looking for a project idea should take a shot at this. If you know a Sarif Viewer, [let us know](https://www.twitter.com/exakat/). Until the phpMySarif of all appears, we can already use [sarif web component](https://github.com/Microsoft/sarif-web-component) from Microsoft, and [Github code scanning](https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/integrating-with-code-scanning). Exakat's [Sarif](https://exakat.readthedocs.io/en/latest/Reports.html#sarif) report is compatible with both of them, so we can use it with both. ## Preparing for PHP 8.0 [PHP 8.0 Beta 4](https://www.php.net/archive/2020.php#2020-09-17-1) is out, so it is time to get ready for migration. Now that the feature freeze is past (since July, mind you), [behavior changes and deprecations](https://php.watch/versions/8.0) are being surveyed. Exakat is already working hard collecting those incompatibility, which will prevent current code from migrating without a change. Although those roadblocks are not too many, some are significant. More important, they may be caught now, as we speak. The list includes : - [Concat and Addition Exchange Priorities](https://exakat.readthedocs.io/en/latest/Rules.html#concat-and-addition) - [PHP 4 constructors are gone](https://exakat.readthedocs.io/en/latest/Rules.html#old-style-constructor) - [Incompatible Method Signatures](https://exakat.readthedocs.io/en/latest/Rules.html#incompatible-signature-methods) - [(unset) Usage](https://exakat.readthedocs.io/en/latest/Rules.html#cast-unset-usage) - [Mismatch Parameter Name](https://exakat.readthedocs.io/en/latest/Rules.html#mismatch-parameter-name) To run a quick check, use the following commands ([Exakat installation](https://exakat.readthedocs.io/en/latest/Installation.html), just in case). ``` php exakat.phar init -p myProject -R projectUrl php exakat.phar project -p myProject php exakat.phar report -p myProject --format Text -T CompatibilityPHP80 ``` The result will be displayed on the standard output, unless you mention the `-f `option, which will save it to a file called `filename` in the `projects/myProject/` directory. If you're not already there, you may also try Compatibility74 or Compatibility73, for example. # The Weekly Audits: 2020, Week #41 Exakat includes a 'weekly' report: this report is built with a selection of five analyses. This means a short audit report, with few issues to review. This is not a lot to read them, and review them in your code. Everyone in the PHP community can focus on one of the classic coding problems and fix it. Talk about the weekly audit around you: you'll find programmers facing the same challenges. To obtain the 'weekly' audit, run an audit, and request the 'Weekly' report. ``` # Init the project (skip when it is already done) php exakat.phar init -p -R https://github.com/Seldaek/monolog.git -git # Run the project (skip when it is already done) php exakat.phar project -p # Export the weekly project (every Monday) php exakat.phar report -p -format Weekly # Open projects//weekly/index.html in your browser ``` Every week, you can find here 5 new analysis to review in your code. In fact, when your code is clean, you can also take a quick look at the upcoming analysis. Weekly recommendations for PHP code review : 2020, week 2020-41 - [Only Variable Passed By Reference](https://exakat.readthedocs.io/en/latest/Rules.html#only-variable-passed-by-reference) : When an argument is expected by reference, it is compulsory to provide a container. - [Avoid Using stdClass](https://exakat.readthedocs.io/en/latest/Rules.html#avoid-using-stdclass) : `stdClass` is the default class for PHP. - [Empty Interfaces](https://exakat.readthedocs.io/en/latest/Rules.html#empty-interfaces) : Empty interfaces are a code smell. - [Assigned Twice](https://exakat.readthedocs.io/en/latest/Rules.html#assigned-twice) : The same variable is assigned twice in the same function. - [Slice Arrays First](https://exakat.readthedocs.io/en/latest/Rules.html#slice-arrays-first) : Always start by reducing an array before applying some transformation on it. # Happy PHP Code Reviews All the 407 analyzers are presented in the docs, including the truculent : [Invalid Pack Format](http://exakat.readthedocs.io/en/latest/Rules.html#invalid-pack-format). It spots invalid formulas in pack() calls. It's a rare bug, but PHP won't check for validity until execution, so it is worth checking before. You can check all of the Exakat reports at the gallery: [exakat gallery](https://www.exakat.io/exakat-gallery). Download Exakat on exakat.io, install it with [Docker](https://hub.docker.com/r/exakat/exakat/), upgrade it with 'exakat.phar upgrade -u' and like us on [github](https://github.com/exakat/exakat). --- # Exakat 1.9.9 Review Source: https://www.exakat.io/exakat-1-9-9-review/ # Exakat 1.9.9 Review ![](https://178.62.231.40/wp-content/uploads/2019/10/grass-elephpant.320.jpg) Exakat focuses on automated updates this week : two new reports (and sections) provide recommendations to use Rector and Php-cs-fixer for fixing, en masse, issues found during an audit. Also, no less than [9 new rules](https://www.exakat.io/exakat-changelog/) were added to Exakat, and a new inventory for mbstring encoding.  The Exakat 1.9.9 review is the best interpreter of the source code. ## Fixing code automatically While Exakat focuses on finding the most intricate bugs, other tools are able to fix en masse code. Among such tools, [Rector](https://getrector.org/) and [PHP-cs-fixer](https://cs.symfony.com/) are doing a fine job, taking some laborious work from our hands. [Rector](https://getrector.org/) offers 'Instant Upgrades and Instant Refactoring of any PHP 5.3+ code'. It is the masterpiece of [Tomas Votruba](https://twitter.com/VotrubaT) and a large community. There is an ever-growing list of [rectors](https://github.com/rectorphp/rector/blob/master/docs/rector_rules_overview.md), which may also be detected with Exakat. From there, Exakat may produce the necessary Rector configuration to fix the code automatically.  After running an Exakat audit, you may access the Rector configuration in the Ambassador report, in the section called 'Fixes'. Copy/paste the configuration in your rector.yaml file, and update the code. You may also invoke the results directly, with the following command :  php exakat.phar report -p rector -format Rector -v The [PHP-cs-fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) "tool fixes your code to follow standards; whether you want to follow PHP coding standards as defined in the PSR-1, PSR-2, etc., or other community-driven ones like the Symfony one." It is authored by [FriendsOfPHP](https://github.com/FriendsOfPHP). Some of the [numerous](https://github.com/FriendsOfPHP/PHP-CS-Fixer#usage) fixes go beyond simple coding standard, and suggest using Date Immutable instead of Datetime, or help with EREG to PREG migration. A lot of those fixes are also detected by Exakat : a configuration file for PHP-CS-FIXER may be generated, based on the analysis found with Exakat. After running an Exakat audit, you may access the PHP-CS-FIXER configuration in the Ambassador report, in the section called 'Fixes'. Copy/paste the configuration in your .php_cs file, and update the code. You may also invoke the results directly, with the following command :  php exakat.phar report -p rector -format Phpcsfixer -v ## Typhinting Usage and Statistics Exakat 1.9.9 collects usage statistics for Typehinting. This cover several questions about typehinting :  - The number of methods using typehint- Which typehint is used : argument or return (properties will come later)- Which definitions use typehint : methods, functions, closures, arrow functions (with php 7.4)- which scalar typehints are used We're using our test corpus of 1700 applications to collect such information, and we'll publish a full review later. In the meantime, send us a note if you have some specific information you'd like to know about typehint usage. From the first results, 25% of PHP code is using type hinting, with 60% of them being scalar. Most common typehint is 'array'. Figures may change by the end of the survey!  ## Encoding inventory A new inventory has joined the large family of inventories : mbstring encoding. Encoding are used with numerous [mbstring](https://www.php.net/mbstring) functions, such as [mb_stripos](https://www.php.net/manual/en/function.mb-stripos.php) or [mb_strtolower](https://www.php.net/manual/en/function.mb-strtolower.php) : also, [Don't miss the third arg](https://exakat.readthedocs.io/en/latest/Rules.html#mbstring-third-arg)! Here, we can see that `None` is also written `NONE`, and `none`, `UTF-8` is also named `utf8` and `utf-8`. There is also the `ASCII`, which is actually named with its alias, `us-ascii`. There are other inventories : SQL, dates, formats, Regex, etc.  # The Weekly Audits: 2019, Week #41 Exakat includes a 'weekly' report: this report is built with a selection of five analyses. This means a short audit report, with few issues to review. This is not a lot to read them, and review them in your code. Everyone in the PHP community can focus on one of the classic coding problems and fix it. Talk about the weekly audit around you: you'll find programmers facing the same challenges. To obtain the 'weekly' audit, run an audit, and request the 'Weekly' report. # Init the project (skip when it is already done) php exakat.phar init -p -R https://github.com/Seldaek/monolog.git -git # Run the project (skip when it is already done) php exakat.phar project -p # Export the weekly project (every Monday) php exakat.phar report -p -format Weekly # Open projects//weekly/index.html in your browser Every week, you can find here 5 new analysis to review in your code. In fact, when your code is clean, you can also take a quick look at the upcoming analysis.  Weekly recommendations for PHP code review : 2019, week 2019-42 - [Class Function Confusion](https://exakat.readthedocs.io/en/latest/Rules.html#class-function-confusion) : Avoid classes and functions bearing the same name.- [Incompatible Signature Methods](https://exakat.readthedocs.io/en/latest/Rules.html#incompatible-signature-methods) : Methods should have the same signature when being overwritten.- [Hidden Use Expression](https://exakat.readthedocs.io/en/latest/Rules.html#hidden-use-expression) : The use expression for namespaces should always be at the beginning of the namespace block.- [Wrong Access Style to Property](https://exakat.readthedocs.io/en/latest/Rules.html#wrong-access-style-to-property) : Use the right syntax when reaching for a property.- [Not Not](https://exakat.readthedocs.io/en/latest/Rules.html#not-not) : Double not makes a boolean, not a `true`. # Happy PHP Code Reviews  All the 390 analyzers are presented in the docs, including the mobile : [Common Alternatives](http://exakat.readthedocs.io/en/latest/Rules.html#common-alternatives): in a conditional structure, expressions were found that are common to both 'then' and 'else'. This is a classic bug, with more than 61% of chance to appear.  You can check all of the Exakat reports at the gallery: [exakat gallery](https://www.exakat.io/exakat-gallery). Download Exakat on exakat.io, install it with [Docker](https://hub.docker.com/r/exakat/exakat/), upgrade it with 'exakat.phar upgrade -u' and like us on [github](https://github.com/exakat/exakat). --- # Growing Old With PHP Source: https://www.exakat.io/growing-older-with-php/ # Getting Older With PHP Exakat runs continuous audits on over 1750 Open source applications, components and frameworks. This is a reality torture test : any error or unexpected results are reviewed and help update the analysis library and the Exakat engine itself. This is a golden mine of tests and real code : an exceptional complement to unit tests. Many, many thanks to all the developers of the PHP community! It is also a great vantage point to observe how we are growing old with PHP. ## Sourcing PHP Codes Those applications are accumulated as the days passes : from security web sites ([www.phpsecure.info](https://www.phpsecure.info/pNews)), [PHP on reddit](https://www.reddit.com/r/PHP/), [PHP weekly](http://phpweekly.com/) interesting applications, or serial authors like [Nino Maduro](https://github.com/nunomaduro) or [Mathieu Napoli](https://github.com/mnapoli). It takes some luck to realize that [Open Cimetiere](http://www.openmairie.org/catalogue/opencimetiere/opencimetiere) is an Open Source graveyard management system, written in PHP. Although new code bases are welcome and sought after so as to check the new PHP 8.0 features in real code ([Keep them coming!](https://www.twitter.com/exakat), some very old code bases are in this corpus, so as to ensure that fantastic syntaxes are still correctly managed. ## Last Commits Per Year This lead to an interesting question : however this wide range of projects is neded for quality control with Exakat, what does it tell us about the impact of time on PHP code bases? To check this, we collected the date of last commit on 1161 projects. We took the date of the last project commit, and measured the interval with now. [![Age of the last commit, in PHP OSS repositories, by year](https://www.exakat.io/wp-content/uploads/2020/11/yearly.aging_-1024x748.png)](https://www.exakat.io/wp-content/uploads/2020/11/yearly.aging_.png) In terms of years, about 45% were updated during the last year, and 77% were within two years. It decreases exponentially, as expected : the older projects are rarer. ## Last Commits By Month We also detailed the same dates by monthly intervals, instead of a year. This lead to a more fine-grained graph. Again, we see 2 peaks : the first dates back from 5 months ago, which is May 2020. It is not clear if we can link this to the pandemic and to more contributions before the lock down relaxing from June.   [![Age of the last commit, in PHP OSS repositories, by month](https://www.exakat.io/wp-content/uploads/2020/11/monthly.aging_-1024x700.png)](https://www.exakat.io/wp-content/uploads/2020/11/monthly.aging_.png) The second peak dates back one year ago, exactly. Last year, early November, we were also getting ready for the next version of PHP : PHP 7.4. This sparked a frenzy of update, which stopped after the publication. Interestingly, this shows that a significant number of PHP projects are stable and mature. They may require some maintenance for forward compatibility, but they don't need more care later. ## Commits by weeks and days Finally, we broke down the data to weeks and days : this ended up as a white noise, with nothing significant emerging. Apparently, the cycle of development of PHP projects focuses around its version, and not holidays or yearly events. Even thursdays, which are the official day for new minor versions, doesn't appear in those stats. ## What About A 'Commit Day' for 2021? Not even PHP birthday's, on June 8th, create a commit fever. Maybe we could declare it the year's 'Commit day' and make it a special moment next year! What a celebration! --- # Prepare for PHP migration with Exakat Source: https://www.exakat.io/prepare-for-php-migration-with-exakat/ ## Prepare for PHP migration with Exakat Exakat detects compatibility issues in your code, for each minor PHP versions since PHP 5.3. It scans the source for function apparence or disappearance, new syntax and behaviors. Here is how to prepare PHP migration with Exakat. ## init-project-done With a local docker system and access to your code, you can use the following command line to execute Exakat. : `docker run -it --rm -v $(pwd)/projects:/usr/src/exakat/projects exakat/exakat:latest exakat init -p sculpin -R git@github.com:sculpin/sculpin.git` This creates a folder called 'projects' in your local directory, and initialize the project called 'sculpin' in it. In particular, it clones the code, from the github repository. Then, run the audit itself : `docker run -it --rm -v $(pwd)/projects:/usr/src/exakat/projects exakat/exakat:latest exakat project -p sculpin` By default, it will run the classic 'Diplomat' report on the code. You can learn about it in the docs. Once this is done, the results is available in the projects/sculpin/diplomat report. ## Audit configuration Now is a good time to audit different sources : - `-p` : the name of the project. Arbitrary name, that you will remember later. - `-R` : the locator for the source. - `--format` : format of the [report](https://exakat.readthedocs.io/en/latest/Reference/Reports.html#list-of-reports), in a machine readable format. For example, [Perfile](https://exakat.readthedocs.io/en/latest/Reference/Reports.html#perfile) - `-T` : the name of the [Rulesets](https://exakat.readthedocs.io/en/latest/Reference/Rulesets.html#list-of-rulesets) rulesets you want to produce. For example, [CompatibilityPHP84](https://exakat.readthedocs.io/en/latest/Reference/Rulesets.html#compatibilityphp80) `docker run -it --rm -v $(pwd)/projects:/usr/src/exakat/projects exakat/exakat:latest exakat project -p sculpin -v` After running the 'project' commands, it is possible to access the individual reports without running again the audit. After running the 'project' commands, it is possible to access the individual reports without running again the audit. Some results are produced directly to the standard output : - PHP 8.0 compatibility`docker run -it --rm -v $(pwd)/projects:/usr/src/exakat/projects exakat/exakat:latest exakat report -p sculpin --format Perfile -T CompatibilityPHP80` - PHP 8.0 security`docker run -it --rm -v $(pwd)/projects:/usr/src/exakat/projects exakat/exakat:latest exakat report -p sculpin --format Perfile -T Security` Some results are writen on the disk, in the projects/sculpin/ directory, in a specific file. - PHP compilation directives`docker run -it --rm -v $(pwd)/projects:/usr/src/exakat/projects exakat/exakat:latest exakat report -p sculpin --format Phpcompilation` More details in the [documentation](https://exakat.readthedocs.io/en/latest/Reference/Reports.html#list-of-reports). --- # PHP top 100 Source: https://www.exakat.io/top-100-php/ # PHP Top 100 Thise is a list of top 100 of the most popular structures in PHP. All these structures are surveyed in a corpus of more than 3000 open source projects, and sorted by usage frequency. - [Top 100 PHP functions](https://www.exakat.io/en/the-100-php-functions-in-2024/) - [Top 100 PHP classes](https://www.exakat.io/en/top-100-php-classes-that-you-should-know/) - [Top 100 PHP native constants](https://www.exakat.io/en/top-100-most-used-php-constants/) - [Top 5 PHP attributes](https://www.exakat.io/en/php-native-attributes-quick-reference/) - [PHP extended classes](https://www.exakat.io/en/php-extended-classes/) - Top 100 PHP interfaces (TBP) - Top 100 PHP traits : N/A - Top 100 PHP enumeration : N/A   ## How to use top 100 Top 100 list the most popular native PHP structures: they are the one any developer is most expected to meet them. Instead of learning them all, it is recommended to focus on these frequent ones first, and then, move to the more discreet ones. ## Not yet a top 100 Some features have a plethora of native versions, such are functions, constants, classes. On the other hand, other structures are still rarely defined by PHP itself, such as enums, traits or attributes. They will grow with time: see how attributes have gained native versions since PHP 8.0? ## Versions of top 100 across the years These top 100 are updated on an irregular base, but they stand up to date in this page. ## Missing top 100 feature? Do you want to see another top 100 featured here?[ Drop us a note](https://phpc.social/@dseguy)!   --- # PHP 8 Attribute usage in 2023 Source: https://www.exakat.io/php-8-attribute-usage-in-2023/ # PHP 8 attributes in 2023 PHP 8 introduced the attributes, in lieu of the phpdoc comments. They may be added to classes, methods, functions, parameters, properties and class constants and provide a PHP-way to write custom configurations. This is a way to provide contextual information inside the code, and make it available to the PHP engine itself. 30 months later, how are the attributes doing? Let's take a look in the open source world, where the code may be publicly audited. ## Code penetration Out of 2900 open source project tested, 137 were sporting one or more attributes. This makes about 4% of all tested projects. For comparison, the spaceship operator <=> is currently adopted by 6%, and started many years ago. Note: last year was about 16% adoption, with a much smaller corpus. ## Popular attributes Here are the most popular attributes, in order of usage occurrences : - Package - ReturnTypeWillChange - Route - Attribute - Pure - AsCommand - Tuleap\Plugin\ListeningToEventClass - IsGranted - Tuleap\Plugin\ListeningToEventName - ParamConverter A total of 175 different attributes were found. ## PHP attributes In PHP 8.2 and 8.3 (so far), there are [four attributes](https://www.exakat.io/en/php-native-attributes-quick-reference/). - Attribute - ReturnTypeWillChange - SensitiveParameter - AllowDynamicProperties They all appear in the top 10, except for SensitiveParameter. ## Attributes lists Last year, we reported attributes lists, edited by independant editors, such as [`PHPStorm`](https://blog.jetbrains.com/phpstorm/2020/10/phpstorm-2020-3-eap-4/), [`Navarr`](https://github.com/navarr/attribute-dependency), `[PHP Language Extensions](https://github.com/DaveLiddament/php-language-extensions)`. Except for `Pure`, which is in the Top 5, those attributes are used but with a very low level of popularity. No static analysis tool provide a list of supported attributes. They usually rely comment-based configuration, which may be adapted down to any line of code, rather than selected definitions. ## Framework attributes On the other hand, the most commonly used attributes are the one depending on a framework. This is the case for `Package`, from Shopware, or `Route`, from Symfony, `AsCommand` from Contao, or `Tuleap\*` from Tuleap (sic). In fact, rare are the attributes that are used beyond one or more distinct project, unless they are closely related, such as a module or an application using a framework. In fact, the three most common PHP attributes per distinct projects are `AllowDynamicProperties`, `Attribute` and `ReturnTypeWillChange`. The next ones are `AsCommand` and `ArrayShape`, with a much lower level of usage. ## Attributes types The vast majority of attributes are set on the class level, and then, at the method levels, including functions, closures and arrow functions. Argument level also exist, though are the rarest form. Property and class constants attributes are not reported in this study. In fact, during the writing of this article, I also discovered that enum, interfaces, traits and enum's cases may also have attributes. This is not documented so far. ## Projects using attributes Here are the OSS projects using the most attributes : - [Shopware](https://www.shopware.com/) - [Kbin](https://kbin.pub/en) - [Tuleap](https://www.tuleap.org/) - [Elephox](https://elephox.dev/) - [Symfony](https://symfony.com/) - [Packagist](https://packagist.org/) - [Contao](https://contao.org/) - [HyperPerf](https://github.com/hyperf/hyperf) - [Windwalker](https://windwalker.io/) - [Concrete5](https://www.concrete5.org/) # Future of the Attributes Attributes are still in the adoption phase. The main publishers of attributes are PHP itself, for its own usage, and frameworks editors. Static analysis is lagging in terms of adoption, by lack of standard to describe common information. --- # PHP switch best practices Source: https://www.exakat.io/well-structured-switch-command-in-php/ ## Ensuring a Well-Structured Switch Command in PHP In PHP, the `switch` statement is a powerful tool for controlling program flow, especially when dealing with multiple conditional branches. However, achieving a clean and efficient switch has its own snags. This post explores the essential switch best practices: tips for checking the quality of a 'switch' command to ensure code quality and optimize its performance. ## Missing Default Entry The first aspect to consider is whether the `switch` statement includes a `default` case. A `default` acts as a fallback option, ensuring that the code executes when none of the defined `case` match. `default` should always be present. In fact, the command `match`, which is an alternative version of `switch`, throws an exception when it cannot match any of the `case`, and the `default` is missing. `default` serve different purposes : - Handle all other cases as one. This is when a finite list of cases is available, and anything outside that list can be handled in a single manner. - Raise an error if ever is it reached. In this situation, the context preceding the `switch` ensures that a finite list of cases is handled. Then, whenever a case outside the legit ones reach the command, an exception should be raised. Always ensure that switch has a default. ## Duplicate Cases It's rewarded to examine a `switch` statement for duplicate cases. PHP only process the first case that it encounters, ignoring the others. This leads to dead code, with some of the branches in the switch to be ignored and never used. Duplicates happen often in very long list of cases. They also happen when the actual value is hidden by constant names, or also, by PHP's type juggling. Indeed, `switch` uses a relaxed comparison style, and some literals may actually be the same. Carefully review the cases values and remove the duplicates. ## Duplicate Code Blocks Another common issue is the repetition of code blocks in different case branches. This redundancy can bloat your `switch` and make it challenging to update in the future. Consider refactoring your code to eliminate duplication and merge the cases. ## Usage of Enumeration Cases PHP has built-in support for [enumerations](https://www.php.net/manual/en/language.types.enumerations.php). One of the important advantages of enumerations is their finite nature. They represent a full collection of options, and no other option shall exist. This makes it easy to check if a `switch` is covering all the possible situations. ## Optimization with Simple Switch PHP 7.2 introduced an optimisation for `switch`, called [simple switch](https://php-dictionary.readthedocs.io/en/latest/dictionary/simple-switch.ini.html). Until then, PHP would review each cases one after the other, and stop as soon as it finds a matching value. With PHP 7.2 and later, PHP sets up a lookup table for the case values, when they are simple literals. Then, PHP jumps immediately to the right case, and bypass all other non-matching cases. Simple switch happens when all the cases are simple values, that can be directly compared to a incoming variable. If the switch has a mix of expressions and simple values, it is recommended to separate the simple literals in a first `switch`, and put the others in a second switch. It might also be more convenient to process them in a different manner, with a previous condition, for example. ## Switch() best practices In conclusion, a well-structured `switch` statement in PHP can greatly enhance your code's clarity, maintainability, and performance. By checking for missing default entries, eliminating duplicate cases and code blocks, leveraging enumeration-like structures, and optimizing complex switches, you can ensure that your `switch` statements remain an efficient and effective part of any PHP codebase. Happy PHP code auditing --- # New error messages in PHP 8.4 Source: https://www.exakat.io/new-error-messages-in-php-8-4/ # New PHP error messages in PHP 8.4 The upcoming PHP 8.4 is bringing new errors messages, and, also, removing some of them. The new messages originate from new features, deprecated ones, removed ones, and extra checks throughout the source code. Let’s review the new PHP error messages in PHP 8.4 and get our code ready for November 2024. # The big PHP error database This post is build on top of the** [PHP error message database](https://php-errors.readthedocs.io/),** which collect the PHP error messages, along with more context information, version information and possible solutions. If you like reading error messages, this is a great link. It is used to build a number of [Exakat](https://www.exakat.io/) static analysis rules. # Evolution of error message counts [![PHP error message count, per version](https://www.exakat.io/wp-content/uploads/2024/11/error-message-count.php84-300x216.png)](https://www.exakat.io/wp-content/uploads/2024/11/error-message-count.php84.png)With distinct 1919 error messages, PHP 8.4 is ranking number 1 most verbose PHP version ever! Although, the trend is quite steady from the last versions, and we can bet that the next PHP 8.5 will have even more error messages. Note that this graph is very different from last year, which counted 783 errors in PHP 8.3: this one is now counting 1731. This is due to an upgrade in the counting application, which is covering new calls to error functions. The teachings are still the same, though. # New error messages PHP 8.4 adds 311 new error messages, and removes 123 of them, for a net increase of 188. A good number of the error messages are checking for incoming values in parameters, and look like `must be a bitmask of IMAP_GC_TEXTS, IMAP_GC_ELT, and IMAP_GC_ENV`, or `Cannot clone unconstructed MessageFormatter`. We will set them aside, for the sake of avoiding repetitive remarks. ## %s(): Implicitly marking parameter $%s as nullable is deprecated, the explicit nullable type A nullable parameter accepts `null` as value and, at least, another type. It is visible with the `?` or `null` in the type specification of the parameter. Yet, it is also possible to define a `null` default value, and not make the type nullable, by simply using `= null` in the parameter definition. Here, the nullable is not explicitly part of the type, but the method does accept it nonetheless. Implicit nullable is not possible with properties, or return values. They yield errors at compile or execution time, respectively. Since PHP 8.4, the implicit nullable parameters are reported by PHP, as a deprecated feature. They will disappear in PHP 9, or in a future version. It is time to make them explicit, or clean up the type specification. This has chances to be the most popular PHP 8.4 error message. **[=> Implicitly marking parameter $%s as nullable is deprecated, the explicit nullable type must be used instead](https://php-errors.readthedocs.io/en/latest/messages/%25s%28%29%3A-implicitly-marking-parameter-%24%25s-as-nullable-is-deprecated%2C-the-explicit-nullable-type-must-be-used-instead.html)** ## Cannot unset hooked property %s::$%s Hooked properties are a new features of PHP 8.4 : a property may have a `get` and/or a `set` hook, defined with the property, that serve as accessors. Normal non-static properties, as in without a hook, may be unset at any time. They are reset to the uninitialized state, and should be be used except for writing or checking with `isset`. On the other hand, hooked properties never can be removed. [=> Cannot unset hooked property %s::$%s](https://php-errors.readthedocs.io/en/latest/messages/cannot-unset-hooked-property-%25s%3A%3A%24%25s.html) ## Must not use parent::$%s::%s() outside a property hook Property hooks are defining methods, at the property level. These methods behave like other methods, and should be able to access the parent method, when the visibility is not private. This means that the child class may use a parent's property hook, and doesn't have to redefine them every time. This is also an example of using the `::` famous operator twice in a row: one to access the parent property, and the second to reach its hook. Now, that syntax is not allowed outside a property hook, even within the same class: no other method shall call it, or, another property. **[=> Must not use parent::$%s::%s() outside a property hook](https://php-errors.readthedocs.io/en/latest/messages/must-not-use-parent%3A%3A%24%25s%3A%3A%25s%28%29-outside-a-property-hook.html)** ## Interfaces may only include hooked properties This error message replaces the previous `Interfaces may not include properties`. Until PHP 8.4, interfaces could not define properties: only methods and constants. Now, with hooked properties being properties tied to methods (`get` and `set`), it is possible to put these hooks in an interface, without a body. Since it is always possible to implement the obvious `get => $this->q` property hook, it is now trivial to include properties in PHP interfaces. **[=> Interfaces may only include hooked properties](https://php-errors.readthedocs.io/en/latest/messages/interfaces-may-only-include-hooked-properties.html)** ## contains CR character that is not allowed in the header There is a family of characters that are now reported when used in a mail header. CR (carriage return), LF (line feed), CRLF (both previous ones), and NULL (aka, `\0`). They are linked with possible hack that introduces extra email addresses or even full mail headers in the message. Basically, if certain mail headers are not sanitized enough, PHP has your back. From the tests, I found `Reply-to` and custom `foo` headers are reported. ## Power of base 0 and negative exponent is deprecated It is now explicitly forbidden to take a negative exponent over 0. A negative exponent of `$x` is the same a taking the positive exponent of `1/$x`: the latter makes no mathematical sense. It used to make PHP sense, and be `INF`, unlike the division by zero, which emitted a saner `DivisionByZeroError`. **[=> Power of base 0 and negative exponent is deprecated](https://php-errors.readthedocs.io/en/latest/messages/power-of-base-0-and-negative-exponent-is-deprecated.html)** ## A never-returning function must not return Using the `never` return type means that the function will end the script, with `die`, or `throw`an exception. In both cases, the `return` clause would not be reached, and it is actually wrong to include one in such a method. In fact, even is the `return` command is unreachable, PHP will complain about it. ## %s(): never-returning function must not implicitly return The previous error message is emitted at compile time: with an explicit `return` command, it is easy to spot a potential problem. Yet, PHP doesn't need the `return` command: when omitted, it will simply return `null`. When the return is implicit, the error will happen at execution time. **[=> A never-returning function must not return](https://php-errors.readthedocs.io/en/latest/messages/never-returning-function-must-not-implicitly-return.html)** ## A function with return type must return a value Finally, with returned types, PHP now reports attempts at returning nothing, while typing the method. `return` with no explicit value means `return null;`. Although, `return` and `return null` do not return the same `null`: the second definition below also yields the error: the `null` value must be explicit. Finally, a typed method, with a returned literal value, is only checked at execution time. Hence, the last code compiles, but will fail at execution time. **[=> A never-returning function must not return](https://php-errors.readthedocs.io/en/latest/messages/returning-by-reference-from-a-void-function-is-deprecated.html)** ## Array and string offset access syntax with curly braces is no longer supported This error message is actually removed from PHP. So, believe it or not, PHP 8.4 is totally dropping the support of curly braces on arrays and strings! Until PHP 8.3, it was a deprecated feature, and in PHP 8.4, it is now a generic `syntax error`. No need to wait for PHP 9.0! **[=> Array and string offset access syntax with curly braces is no longer supported](https://php-errors.readthedocs.io/en/latest/messages/array-and-string-offset-access-syntax-with-curly-braces-is-no-longer-supported.html)** ## Unknown errors Here are some errors which are in the source code of PHP, but we could not reproduce. They are often niche error, deep inside a PHP extension, and are lacking context to be well understood. In fact, they are just fun to read, out of context. - `Not yet implemented` - `individual body cannot be empty` - `Unknown and uncaught modification type.` - `CPU doesn't support SSE2` - `Cannot use variable $%s twice` Actually, this happens when the same variable is used twice (or more) in the use expression of a closure ## Previous error message reviews - [New error messages in PHP 8.3](https://www.exakat.io/en/new-php-error-messages-in-php-8-3/) - [New error messages in PHP 8.2](https://www.exakat.io/en/new-php-errors-messages-in-php-8-2/) - [New error messages in PHP 8.1](https://www.exakat.io/en/new-error-messages-in-php-8-1/) - [Common PHP 8.0 Error Messages](https://www.exakat.io/en/common-php-8-0-compilation-error-messages/) # Get ready for PHP 8.4 Hopefully, this quick tour in the new error messages of PHP 8.4 will help preparing the code for its own migration to PHP 8.4. Since some of the checks are only available at execution time, it is worth running static analysis on it to spot potential errors. Exakat include the Compatibility PHP 8.4 ruleset, with rules dedicated to PHP 8.4. They can run on PHP 8.3, so as to be ready for November 2024. Happy auditing. --- # PHP Framework list Source: https://www.exakat.io/php-framework-list/ # PHP frameworks A curated list of 89 PHP frameworks. ## Contributing See [CONTRIBUTING](https://github.com/exakat/php-frameworks/blob/master/CONTRIBUTING.md), to add or update a listing. ## PHP Framework list In alphabetical order. - **A** [**Agavi**](https://github.com/agavi/agavi) : Agavi is a powerful, scalable PHP5 application framework that follows the MVC paradigm - [**Aura**](https://auraphp.com/) Advanced tools for advanced applications. - **B** [**Banshee**](https://www.banshee-php.org/) : the secure PHP framework. - [**B2evolution**](https://b2evolution.net/) : b2evolution includes everything you need to build websites for sharing and interacting with your community. - [**Bludit**](https://www.bludit.com/) : Create your own Website or Blog in seconds, Simple, Fast, Secure, Flat-File CMS - [**bullet PHP**](http://bulletphp.com/) : Bullet is a functional PHP micro-framework that helps you easily create REST APIs and web applications that automatically conform to the requirements of the HTTP specification. - **C** [**Cakephp**](https://cakephp.org/) : Build fast, grow solid | PHP Framework - [**Cappucino**](https://www.cappuccino.dev/) : Cappuccino is an open source framework that makes it easy to build desktop-caliber applications that run in a web browser. - [**chubbyPHP**](https://github.com/chubbyphp/chubbyphp-framework.git) : A minimal, highly performant middleware PSR-15 microframework built with as little complexity as possible, aimed primarily at those developers who want to understand all the vendors they use. - [**codeigniter**](https://www.codeigniter.com/) : The small framework with powerful features - [**concrete**](https://www.concretecms.org/) : Do you want a CMS that both developers and editors love? - [**contao**](https://contao.org/en) : Contao is a powerful open source CMS that allows you to create professional websites and scalable web applications - [**crossPHP**](https://www.crossphp.com/#home) : API优先 - **D** [**Drupal**](https://www.drupal.org/) : the best digital experience platform(DXP) on the web, proudly open source. - [**DuckPHP**](https://github.com/dvaknheo/duckphp.git) : the best digital experience platform(DXP) on the web, proudly open source. - **E** [**e107**](https://e107.org/) : Functionality For Every Occasion - [**elephpantCMS**](https://github.com/jbroadway/elefant.git) : Functionality For Every Occasion - [**elephox**](https://github.com/elephox-dev/) : A type-safe PHP framework - **F** [**Fatfree**](https://fatfreeframework.com/3.8/home) : powerful yet easy-to-use PHP micro-framework designed to help you build dynamic and robust web applications - fast! - [**FeastFramework**](https://github.com/FeastFramework/framework) : Fast, Easy, Agile, Slim, Tested and Trans-Fat Free. - [**Flextype**](https://github.com/flextype/flextype) : Flextype is an open-source Hybrid Content Management System with the freedom of a headless CMS and with the full functionality of a traditional CMS - [**Flight**](https://flightphp.com/) : Flight is a fast, simple, extensible framework for PHP. Flight enables you to quickly and easily build RESTful web applications. - [**Flourish**](https://flourishlib.com/) : PHP web application framework focussed on Domain-Driven Design and clean code. - [**Flow**](https://flow.neos.io/) : PHP web application framework focussed on Domain-Driven Design and clean code. - [**FuelPHP**](https://fuelphp.com/) : a fast, simple and flexible PHP 5.4+ framework, born from the best ideas of other frameworks, with a fresh start! - [**Froq**](http://froqframework.com/) : Hassle-free PHP framework. - **G** [**GlowyPHP**](https://awilum.github.io/glowyphp/) : A set of decoupled and reusable PHP building blocks for kickass Web Applications - [**Grav**](https://www.horde.org/) : Grav is a modern open source flat-file CMS - [**GuppyCMS**](https://www.freeguppy.org/) : GuppY, le CMS de référence sans base de données. - **H** [**Horde**](https://www.iceframework.org/) : The Horde Application Framework is a general-purpose web application framework in PHP, - **I** [**Ice Framework**](https://www.iceframework.org/) : Ice - simple, fast and open-source PHP framework frozen in C-extension. - [**Ibexa**](https://www.ibexa.co/) : Ibexa Digital Experience Platform (DXP) helps B2B companies to transform traditional sales strategies into frictionless buying experiences - **J** [**Jaws**](http://jaws-project.com) : Jaws is a Framework and Content Management System for building dynamic web sites. - [**Joomla**](https://www.joomla.org/) : The Flexible Platform Empowering Website Creators - [**Jelix**](https://github.com/jelix/jelix) : Jelix 2 is an open-source framework for PHP. - **K** [**Koseven**](https://github.com/koseven/koseven/) : Koseven is an elegant, open source, and object oriented HMVC framework built using PHP7, by a team of volunteers. (Based on Kohana, which is now deprecated) - **L** [**Laminas**](https://getlaminas.org/) : Laminas Project, the enterprise-ready PHP Framework and components - [**Laravel**](https://laravel.com/) : The PHP Framework for Web Artisans - [**Laravel-zero**](https://github.com/laravel-zero/laravel-zero) : Micro-framework for console applications - [**leafPHP**](https://leafphp.dev/) : Simple and elegant PHP - [**light-php**](https://bakeiro.github.io/Light-PHP-documentation/) : open-source minimal PHP framework that only includes what's necessary - [**lightmvc**](https://lightmvcframework.net/) : very modular, event-driven and Swoole-enabled framework! - [**limonade**](https://limonade-php.github.io/) : Limonade is a PHP micro framework for rapid web development and prototyping. - [**lithium**](https://li3.me/) : fast, flexible and most RAD development framework for PHP - [**lucinda**](https://www.lucinda-framework.com/) :Lucinda: Modular Ultra-High Performance PHP Framework. - [**lumen**](https://lumen.laravel.com/docs/9.x) : Lumen is an open-source PHP micro-framework created by Taylor Otwell as an alternative to Laravel to meet the demand of lightweight installations. - **M** [**Magix-cms**](https://github.com/magix-cms/magixcms-3) : Magix CMS dans sa version 3, incluant une nouvelle librairie et de nouvelles fonctionnalités. - [**Melis**](https://www.melistechnology.com/) : The 1st Open Source Cross-Framework Digital Platform - [**Meedoo**](https://medoo.in/) : The Lightweight PHP Database Framework to Accelerate Development - [**ModX**](https://modx.com/) : The CMS for Business, Not Bloggers - **N** [**Nette**](https://nette.org/en/) : Real Programmers don't Use Frameworks - **O** [**Oro inc**](https://oroinc.com/) : Oro revolutionizes your business with B2B eCommerce Software and CRM. - **P** [**Phalcon**](https://phalcon.io/en-us) : A full-stack PHP framework delivered as a C-extension - [**PHP-deamon**](https://github.com/kakserpom/phpdaemon) : Asynchronous framework in PHP. It has a huge number of features. Designed for highload. - [**PHP-express**](https://github.com/riverside/php-express.git) : PHP micro-framework inspired by Express.js - [**Pixie**](https://phalcon.io/en-us) : The modern component base PHP framework - [**Prado**](http://www.pradoframework.net/site/) : PRADOTM is a component-based and event-driven programming framework for developing Web applications in PHP - **Q** [**Qcodo**](https://github.com/qcodo/qcodo.git) : Qcodo is an open-source PHP web application framework which builds an object-relational model (ORM),[3] CRUD (create, retrieve, update, delete)[4] UI pages, and AJAX hooks from an existing data model. - [**QueryPHP**](https://www.queryphp.com/) High Performance PHP Progressive Framework. - **R** [**Redcat**](https://github.com/redcatphp/redcatphp.git) Rapid Application Development with Scalability and High Performance - **S** [**Silex**](https://github.com/silexphp/Silex) : Silex, a simple Web Framework - [**Silverstripe**](https://www.silverstripe.org/) : Silverstripe CMS is the intuitive content management system and flexible framework loved by editors and developers alike. - [**SimpleMVC**](https://github.com/simplemvc/framework.git) : SimpleMVC is an MVC framework for PHP based on the KISS principle. - [**Simple PHP framework**](https://github.com/johnsonfash/simple-php-framework.git) : Simple php framework is a light weight (< 1mb), expressive, easy to configure, and less ambiguous framework. - [**Slim**](https://www.slimframework.com/) : Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs. - [**Smart Framework**](https://github.com/unix-world/Smart.Framework) : practical, modern and high performance PHP / JavaScript Framework for Web featuring Middlewares + MVC. - [**Spiral Framework**](https://spiral.dev/) : High-Performance PHP/Go Framework - [**Stupidly Simple Framework**](https://stupidlysimple.github.io/) An MVC Framework based on PHP, built for rapid development - [**Symfony**](https://symfony.com/) : Symfony is a set of reusable PHP components and a PHP framework to build web applications, APIs, microservices and web services - **T** [**ThinkPHP**](https://www.thinkphp.cn/) : ThinkPHP框架 - 是由上海顶想公司开发维护的MVC结构的开源PHP框架,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业应用开发而诞生的。 - [**Tine**](https://www.tine-groupware.de/) : Mehr als nur Groupware - [**Trongate**](https://github.com/trongate/trongate-framework.git) : Break the rules use Trongate - [**Typo3**](https://typo3.org/) : the Professional, Flexible Content Management System - **U** [**Ubiquity**](https://github.com/phpMv/ubiquity.git) Ubiquity, a powerful and fast framework for efficient design. - [**UserFrosting**](https://github.com/userfrosting/UserFrosting.git) UserFrosting is a secure, modern user management system written in PHP and built on top of the Slim Microframework, Twig templating engine, and Eloquent ORM. - **V** [**Vanilla**](https://github.com/vanilla/vanilla) : Vanilla was born out of the desire to create flexible, customizable, and downright entertaining community solutions. - **W** [**Wave Framework**](https://github.com/wave-framework/wave.git) : Wave is a lightweight MVC framework written in PHP. - [**Windwalker**](https://windwalker.io) : A PHP Rapid Application Development Framework - [**Webby**](https://webby.sylynder.com/) : A "lego-like" PHP framework for building Simple Applications - [**Wordpress**](https://wordpress.org/) : WordPress is open source software you can use to create a beautiful website, blog, or app. - **X** [**X framework**](https://framework-x.org/) : Simple and fast PHP micro framework. Async made easy. - **Y** [**Yii2**](https://www.yiiframework.com/) : Yii is a fast, secure, and efficient PHP framework. - [**Yaf**](https://www.php.net/manual/en/book.yaf.php) : The Yet Another Framework (Yaf) extension is a PHP framework that is used to develop web applications. - [**YAR**](https://www.php.net/manual/en/book.yar.php) : Yar is a RPC framework which aims to provide a simple and easy way to do communication between PHP applications It has the ability to concurrently call multiple remote services. - **Z** [**Zest Framework**](https://github.com/zestframework/Zest) : Zest is a very light-weight PHP framework without any dependencies except core and autoloader. - [**Zikula**](https://github.com/zikula/core) : build simple one-page websites to individual web applications utilising different types of extensions for making your project to something special. - [**Z-PHP**](http://www.z-php.com/) : PHP 7,8 快速 简洁 安全 易用 - **2** [**299Ko CMS**](https://299ko.ovh/) : 299Ko : Un CMS Flat-File pour tous. ## Retired Some framework which are now retired : either renamed, or discontinued. This is to prevent repeated attempts at adding them in the above list, not a life/death certificate. - [Ez platform](https://ez.no) : Turned ibexo - [Flourish](https://flourishlib.com/) - [Xataface](http://xataface.com/) - [Zend Framework](https://framework.zend.com/) : Zend Framework is now the Laminas Project! # Publication Originally published on [Exakat](https://www.exakat.io/en/php-framework-list/). --- # Adoption levels of recent PHP features Source: https://www.exakat.io/adoption-levels-of-recent-php-features/ # Adoption levels of recent PHP features While browsing the [PHP devsquad](https://app.daily.dev/squads/phpdev), I ran into this article that listed the [top 10 powerful features of PHP](https://medium.com/@hiadeveloper/phps-hidden-treasures-10-powerful-features-you-didn-t-know-you-needed-2cd15b7f400b). Beside the list, it made me wonder what are the adoption levels of recent PHP features, based on this list. Once in a while, I like to check such top 10, to see how main stream PHP features are becoming. After all, there is a step between what is announced at the new version release, and how features are adopted (I'm looking at you, [spaceship operator](https://www.php.net/manual/en/language.operators.comparison.php). And working on [Exakat](https://www.exakat.io/) tends to keep me looking at [exotic](https://php-tips.readthedocs.io/en/latest/tipSection.html) parts of PHP. ## A Top 10 PHP Features Here is the list from the article. Can you find your own favorite features? - [Generators](https://www.php.net/manual/en/language.generators.php) - [Traits](https://www.php.net/manual/en/language.oop5.traits.php) - [Anonymous Classes](https://www.php.net/manual/en/language.oop5.anonymous.php) - [Null Coalesce Operator](https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op) - [Ellipsis (Variadic, ...) Operator](https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list) - [DateTimeImmutable Class](https://www.php.net/manual/en/class.datetimeimmutable.php) - [Iterator](https://www.php.net/manual/en/class.iterator.php) - [Arrow Functions](https://www.php.net/manual/en/functions.arrow.php) - [Named Arguments](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments) - [Match Expression](https://www.php.net/manual/en/control-structures.match.php) ## PHP Features Adoption in Open Source Code Now, here is a survey that check how these features are used, in a corpus of over 3100 PHP open source projects. - Generators 11,00 % - Traits 33,00 % - Anonymous Class 17,00 % - Coalesce 47,00 % - Ellipsis (...) 31,00 % - DateImmutable 16,00 % - Iterator 34,00 % - Arrow Functions 23,00 % - Named Arguments 21,00 % - Match 17,00 % Over one third of adoption in the code for Iterators, Ellipsis Coalesce and traits: to compare with, `arrays` are used in 66% of all projects, so that's a good level, compared to one of the PHP star features. Generators, Match, DateTimeImmutable and Anonymous Classes may need a bit more drum rolling, or more wide spread use cases. There are quite a number of tutorials to create them, but are there enough use cases blogs to help push their usage? When you have some ready, [ping me on dseguy.bsky.social](https://bsky.app/profile/dseguy.bsky.social), or [on the Dev Squad](https://app.daily.dev/squads/phpdev) ! ## Personal adoption of PHP Features I reviewed the list, and, honestly, I can say I have adopted half of them. Generators, Coalesce, ellipsis, named arguments and match are common. I don't work with dates, so that leave DateTimeImmutable out. I have found little use of Traits so far. I'm happy with Closures, in place of Arrow functions, and Generator, in place of Iterators. And I name every [CITE](https://php-dictionary.readthedocs.io/en/latest/dictionary/cite.ini.html) in the code. ## Limitations of the survey The figures above are extracted from analyzing 3100+ PHP open source projects. Such projects may be small or big, and bigger projects tends to use more features. Some may be more recent than others, and some keep backward compatibility very high in the priorities. They include the top 1000 packages, the most common frameworks and applications, and some lesser known. ## PHP is modernizing, one code after the other After a few years, it is interesting to check the adoption level of released features. They become actually adopted once they found their usage in everyone's code, as much as possible. While a top 10 PHP features is probably a bit arbitrary, it is good to see the community choice getting PHP more modern. --- # Avoid array_unique Source: https://www.exakat.io/avoid-array_unique/ ## array_unique() is quite slow It is recommended to avoid array_unique. [array_unique()](http://www.php.net/array_unique) function is a native function that will extract distinct values in an array. [array_count_values](http://www.php.net/array_count_values) is also a native function that will extract distinct values in an array, and count the number of occurrences. array_count_values actually does a lot more work than array_unique by providing a count of the distinct values, on top of finding them. Indeed, extracting the keys from the result of array_count_values removes the counts, and is the same result than array_unique. Using the flag SORT_REGULAR, which makes direct comparisons of the values instead of convertir them to string (default) or integer (SORT_NUMERIC), is even slower. ## Alternatives to avoid array_unique() The strange fact is that array_count_values() is a lot faster than array_unique. It may be twice as fast for small arrays (with tens of values), and it get even faster as the size of the array grows. Some, like [puremango](http://www.puremango.co.uk/2010/06/fast-php-array_unique-for-removing-duplicates/) finally resorted to coding their own function in PHP, achieving much higher speed. The only limitations are that array_count_values can only count string and integer values (as the PHP error will show if some other type is used), and array_unique includes a sorting option. ## avoid array_unique() Avoid array_unique as much as possible and it will speed up your code execution. You may use the array_count_values(), as it is the fastest code, but it is also possible to use other fast alternatives : foreach with [array_keys](http://www.php.net/array_keys), or [array_flip](http://www.php.net/array_flip) (benchmark in this article : '[array_unique vs array_flip](http://stackoverflow.com/questions/8321620/array-unique-vs-array-flip)'). Of course, relying on these alternatives degrades the readability of the code, since, after all, array_unique() is the eponymous feature. It's just a bit slow. --- # Collateral behavior changes with PHP 8.0 switch() Source: https://www.exakat.io/php-8-changes-in-switch/ # Collateral behavior changes with PHP 8.0 switch() PHP 8.0 introduced a modernisation of the comparison between integers and strings. It changes the result of comparisons when a string is compared to an integer. Obviously, it includes comparing 0 and '' (empty string). This means that some comparisons were true in PHP 7, and are not true anymore in PHP 8. Those are the switch() behavior changes with PHP 8.0. This modernisation is a behavior change: an evolution, where the code doesn't change, but its result do. Usually, there is no notice, nor no depreciation message. This must be caught with unit tests. This behavior change is also a poster child of collateral damage in the code. The point of origin is the the comparison operator. Yet, it actually has impact in other features: sometimes very unexpected yet completely related changes. Come with me down the rabbit hole, where today is one of your un-birthday. And probably tomorrow is too. ## String to Number Comparison The starting point is the following table, which explains that the relaxed comparison between strings and integers is changing. In particular, an integer compared to a non-integer string is now false. That makes total sense, and it is definitely a nice modernisation. The details are in the [migration guide](https://www.php.net/manual/en/migration80.incompatible.php), and also in the following table. | Comparison | PHP 7 | PHP 8 | | ---------- | ----- | ----- | | 0 == "0" | true | true | | 0 == "0.0" | true | true | | 0 == "foo" | true | false | | 0 == "" | true | false | | 42 == " 42" | true | true | | 42 == "42foo" | true | false | ## Collateral evolution Obviously, the evolution of comparison between integers and strings impacts the == operator. On the other hand, the === is not affected: in fact, it is a good solution to avoid the migration headaches. Use it in PHP 7, whenever you can. The comparison feature happens to have many other usages. They don't always rely on the == operator. This is the case of in_array() and array_keys(), which search for values in an array; it is also the case with the inegality operators, such as >, <, <=, >=, which have now more values that are different, and so, inegal. And there is the case of switch(). ## switch() identical cases In PHP 7, switch() used a relaxed comparison between its operand and the values in the cases. This means that some cases were actually equal, even though they are different case values. For example: This displays 'Zero', three times in PHP 7. Everything is a zero, because the comparison between 0 and 0, empty string and false values is always true. Amusingly, it works the same for 0, '' (empty string) and false, as long as they are the first case in the switch statement. In PHP 7, all those cases are the same. They all are duplicate cases, and PHP will never read the later cases: only the first one. As a coding rule for PHP 7.4 and older, it is recommended to avoid using any two or more of those falsy values as cases. Otherwise, all but the first are dead code. ## PHP 8 appearing new cases When in doubt, it is always a good idea to follow best practices. In this case, it reduces the amount of dead code in the application. Yet, at PHP 8 migration time, it now creates a bug. In the previous example, we removed the empty string case on account that it is dead code. But it does come back as a full case in PHP 8. Indeed, with the change of comparison results when using == in PHP 8, 0 and '' are not equal anymore. In PHP 7, the above code displays 'Zero' for an empty string. In PHP 8, it displays 'Not zero'. In both cases, we do not see the right result. So, in PHP 8, the identical case rule change. Some special situation, which were dead code in PHP 7, are now fully fleshed case, and should be back in the command. Otherwise, default might actually take care of them, like in the example above. Removing duplicate cases is easy enough, but creating cases to match special situations is a lot more difficult. The business case around the switch may or may not exist. ## The special case of null We have covered changes around 0, empty string and false. One other usual suspect is `null`. What about it ? Null has not changed its behavior in PHP 8, so all is safe. Yet, `null` has specific behavior, which makes is a bit special to handle. It is equal to everything that is empty, yet, this is not an associative relationship. So, we have: ## The case of null and switch() Now, let's go back to switch(). switch() can use `null` as a case. It is a legit value. This makes sense, because 0 is caught by the first case, empty string by the second, and null is caught by 0 again: 0 == null, remember? Now, let's move the cases inside the switch(), and put the null case in first position. This time, everything is null. This means that the position of the null case has a direct impact on the performance of the switch(). Put it first, and it hides 0 and empty string. Put it last, or later, and it is hidden by 0 and empty string. It is probably safer to process the null value before the switch() and avoid using it altogether in the switch command. ## Simple switch? PHP 7.2 introduced a notion of simple switch(): when the cases are all simple distinct literal values, PHP builds a lookup table for the cases, and jump directly to the correct case, instead of reviewing the cases one after each other. This is a nice performance trick. If null was behaving like other literal values, we would not have seen the last behavior, where it handles most of the cases. So, the mere presence of a null case in a switch prevents the optimisation of the simple switch(). Another good reason to avoid using null in switch(). # Conclusion PHP 8.0 updated the comparison mechanism to compare strings and integers. It leads to a number of nuanced but breaking modernisations. More importantly, these changes are pervasive across the language, as comparison is a common operation used by many PHP features, such as switch(). Due to this evolution, the duplicate cases are changing from PHP 7 to PHP 8. Some were totally hidden in PHP 7, and should now be processed explicitely in PHP 8. And in the mean time, the special behavior of null has not been changing. It is often an unknown feature, which may bring special behavior to PHP scripts. Those special situations are very rare, just like switch() usage is not common in PHP applications. Observing them is a good moment to remember the intricacies of the language, and enjoy special features such as simple switch(). --- # List of PHP native interfaces, and their implementation Source: https://www.exakat.io/list-of-php-native-interfaces-and-their-implementation/ # List of PHP native interfaces, and their implementation [![List of PHP native interfaces, and their implementation](https://www.exakat.io/wp-content/uploads/2024/11/forest-path.320-300x300.jpg)](https://www.exakat.io/wp-content/uploads/2024/11/forest-path.320.jpg)This is the list of PHP native interfaces, and their implementation as native classes. This was extracted from the PHP 8.3 source code, with Reflection. Each link leads to the documentation. It is based on a standard **PHP 8.3** distribution, plus a few extensions. Interfaces that are not implemented are not listed here (like BackedEnum) and classes that do not implements any interface are not listed here either (like, Stdclass).       - **[ArrayAccess](https://www.php.net/manual/en/class.arrayaccess.php)** (13) [WeakMap](https://www.php.net/manual/en/class.weakmap.php) - [CachingIterator](https://www.php.net/manual/en/class.cachingiterator.php) - [RecursiveCachingIterator](https://www.php.net/manual/en/class.recursivecachingiterator.php) - [ArrayObject](https://www.php.net/manual/en/class.arrayobject.php) - [ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php) - [RecursiveArrayIterator](https://www.php.net/manual/en/class.recursivearrayiterator.php) - [SplDoublyLinkedList](https://www.php.net/manual/en/class.spldoublylinkedlist.php) - [SplQueue](https://www.php.net/manual/en/class.splqueue.php) - [SplStack](https://www.php.net/manual/en/class.splstack.php) - [SplFixedArray](https://www.php.net/manual/en/class.splfixedarray.php) - [SplObjectStorage](https://www.php.net/manual/en/class.splobjectstorage.php) - [Phar](https://www.php.net/manual/en/class.phar.php) - [PharData](https://www.php.net/manual/en/class.phardata.php) - **[Countable](https://www.php.net/manual/en/class.countable.php)** (24) [WeakMap](https://www.php.net/manual/en/class.weakmap.php) - [DOMNodeList](https://www.php.net/manual/en/class.domnodelist.php) - [DOMNamedNodeMap](https://www.php.net/manual/en/class.domnamednodemap.php) - [ResourceBundle](https://www.php.net/manual/en/class.resourcebundle.php) - [CachingIterator](https://www.php.net/manual/en/class.cachingiterator.php) - [RecursiveCachingIterator](https://www.php.net/manual/en/class.recursivecachingiterator.php) - [ArrayObject](https://www.php.net/manual/en/class.arrayobject.php) - [ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php) - [RecursiveArrayIterator](https://www.php.net/manual/en/class.recursivearrayiterator.php) - [GlobIterator](https://www.php.net/manual/en/class.globiterator.php) - [SplDoublyLinkedList](https://www.php.net/manual/en/class.spldoublylinkedlist.php) - [SplQueue](https://www.php.net/manual/en/class.splqueue.php) - [SplStack](https://www.php.net/manual/en/class.splstack.php) - [SplHeap](https://www.php.net/manual/en/class.splheap.php) - [SplMinHeap](https://www.php.net/manual/en/class.splminheap.php) - [SplMaxHeap](https://www.php.net/manual/en/class.splmaxheap.php) - [SplPriorityQueue](https://www.php.net/manual/en/class.splpriorityqueue.php) - [SplFixedArray](https://www.php.net/manual/en/class.splfixedarray.php) - [SplObjectStorage](https://www.php.net/manual/en/class.splobjectstorage.php) - [Phar](https://www.php.net/manual/en/class.phar.php) - [PharData](https://www.php.net/manual/en/class.phardata.php) - [SimpleXMLElement](https://www.php.net/manual/en/class.simplexmlelement.php) - [SimpleXMLIterator](https://www.php.net/manual/en/class.simplexmliterator.php) - [ZipArchive](https://www.php.net/manual/en/class.ziparchive.php) - **[DOMChildNode](https://www.php.net/manual/en/class.domchildnode.php)** (5) [DOMCharacterData](https://www.php.net/manual/en/class.domcharacterdata.php) - [DOMElement](https://www.php.net/manual/en/class.domelement.php) - [DOMText](https://www.php.net/manual/en/class.domtext.php) - [DOMComment](https://www.php.net/manual/en/class.domcomment.php) - [DOMCdataSection](https://www.php.net/manual/en/class.domcdatasection.php) - **[DOMParentNode](https://www.php.net/manual/en/class.domparentnode.php)** (3) [DOMDocumentFragment](https://www.php.net/manual/en/class.domdocumentfragment.php) - [DOMDocument](https://www.php.net/manual/en/class.domdocument.php) - [DOMElement](https://www.php.net/manual/en/class.domelement.php) - **[DateTimeInterface](https://www.php.net/manual/en/class.datetimeinterface.php)** (2) [DateTime](https://www.php.net/manual/en/class.datetime.php) - [DateTimeImmutable](https://www.php.net/manual/en/class.datetimeimmutable.php) - **[Iterator](https://www.php.net/manual/en/class.iterator.php)** (42) [InternalIterator](https://www.php.net/manual/en/class.internaliterator.php) - [Generator](https://www.php.net/manual/en/class.generator.php) - [IntlIterator](https://www.php.net/manual/en/class.intliterator.php) - [IntlPartsIterator](https://www.php.net/manual/en/class.intlpartsiterator.php) - [RecursiveIteratorIterator](https://www.php.net/manual/en/class.recursiveiteratoriterator.php) - [IteratorIterator](https://www.php.net/manual/en/class.iteratoriterator.php) - [FilterIterator](https://www.php.net/manual/en/class.filteriterator.php) - [RecursiveFilterIterator](https://www.php.net/manual/en/class.recursivefilteriterator.php) - [CallbackFilterIterator](https://www.php.net/manual/en/class.callbackfilteriterator.php) - [RecursiveCallbackFilterIterator](https://www.php.net/manual/en/class.recursivecallbackfilteriterator.php) - [ParentIterator](https://www.php.net/manual/en/class.parentiterator.php) - [LimitIterator](https://www.php.net/manual/en/class.limititerator.php) - [CachingIterator](https://www.php.net/manual/en/class.cachingiterator.php) - [RecursiveCachingIterator](https://www.php.net/manual/en/class.recursivecachingiterator.php) - [NoRewindIterator](https://www.php.net/manual/en/class.norewinditerator.php) - [AppendIterator](https://www.php.net/manual/en/class.appenditerator.php) - [InfiniteIterator](https://www.php.net/manual/en/class.infiniteiterator.php) - [RegexIterator](https://www.php.net/manual/en/class.regexiterator.php) - [RecursiveRegexIterator](https://www.php.net/manual/en/class.recursiveregexiterator.php) - [EmptyIterator](https://www.php.net/manual/en/class.emptyiterator.php) - [RecursiveTreeIterator](https://www.php.net/manual/en/class.recursivetreeiterator.php) - [ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php) - [RecursiveArrayIterator](https://www.php.net/manual/en/class.recursivearrayiterator.php) - [DirectoryIterator](https://www.php.net/manual/en/class.directoryiterator.php) - [FilesystemIterator](https://www.php.net/manual/en/class.filesystemiterator.php) - [RecursiveDirectoryIterator](https://www.php.net/manual/en/class.recursivedirectoryiterator.php) - [GlobIterator](https://www.php.net/manual/en/class.globiterator.php) - [SplFileObject](https://www.php.net/manual/en/class.splfileobject.php) - [SplTempFileObject](https://www.php.net/manual/en/class.spltempfileobject.php) - [SplDoublyLinkedList](https://www.php.net/manual/en/class.spldoublylinkedlist.php) - [SplQueue](https://www.php.net/manual/en/class.splqueue.php) - [SplStack](https://www.php.net/manual/en/class.splstack.php) - [SplHeap](https://www.php.net/manual/en/class.splheap.php) - [SplMinHeap](https://www.php.net/manual/en/class.splminheap.php) - [SplMaxHeap](https://www.php.net/manual/en/class.splmaxheap.php) - [SplPriorityQueue](https://www.php.net/manual/en/class.splpriorityqueue.php) - [SplObjectStorage](https://www.php.net/manual/en/class.splobjectstorage.php) - [MultipleIterator](https://www.php.net/manual/en/class.multipleiterator.php) - [Phar](https://www.php.net/manual/en/class.phar.php) - [PharData](https://www.php.net/manual/en/class.phardata.php) - [SimpleXMLElement](https://www.php.net/manual/en/class.simplexmlelement.php) - [SimpleXMLIterator](https://www.php.net/manual/en/class.simplexmliterator.php) - **[IteratorAggregate](https://www.php.net/manual/en/class.iteratoraggregate.php)** (12) [WeakMap](https://www.php.net/manual/en/class.weakmap.php) - [DatePeriod](https://www.php.net/manual/en/class.dateperiod.php) - [DOMNodeList](https://www.php.net/manual/en/class.domnodelist.php) - [DOMNamedNodeMap](https://www.php.net/manual/en/class.domnamednodemap.php) - [ResourceBundle](https://www.php.net/manual/en/class.resourcebundle.php) - [IntlBreakIterator](https://www.php.net/manual/en/class.intlbreakiterator.php) - [IntlRuleBasedBreakIterator](https://www.php.net/manual/en/class.intlrulebasedbreakiterator.php) - [IntlCodePointBreakIterator](https://www.php.net/manual/en/class.intlcodepointbreakiterator.php) - [ArrayObject](https://www.php.net/manual/en/class.arrayobject.php) - [SplFixedArray](https://www.php.net/manual/en/class.splfixedarray.php) - [PDOStatement](https://www.php.net/manual/en/class.pdostatement.php) - [mysqli_result](https://www.php.net/manual/en/class.mysqli-result.php) - **[JsonSerializable](https://www.php.net/manual/en/class.jsonserializable.php)** (1) [SplFixedArray](https://www.php.net/manual/en/class.splfixedarray.php) - **[OuterIterator](https://www.php.net/manual/en/class.outeriterator.php)** (16) [RecursiveIteratorIterator](https://www.php.net/manual/en/class.recursiveiteratoriterator.php) - [IteratorIterator](https://www.php.net/manual/en/class.iteratoriterator.php) - [FilterIterator](https://www.php.net/manual/en/class.filteriterator.php) - [RecursiveFilterIterator](https://www.php.net/manual/en/class.recursivefilteriterator.php) - [CallbackFilterIterator](https://www.php.net/manual/en/class.callbackfilteriterator.php) - [RecursiveCallbackFilterIterator](https://www.php.net/manual/en/class.recursivecallbackfilteriterator.php) - [ParentIterator](https://www.php.net/manual/en/class.parentiterator.php) - [LimitIterator](https://www.php.net/manual/en/class.limititerator.php) - [CachingIterator](https://www.php.net/manual/en/class.cachingiterator.php) - [RecursiveCachingIterator](https://www.php.net/manual/en/class.recursivecachingiterator.php) - [NoRewindIterator](https://www.php.net/manual/en/class.norewinditerator.php) - [AppendIterator](https://www.php.net/manual/en/class.appenditerator.php) - [InfiniteIterator](https://www.php.net/manual/en/class.infiniteiterator.php) - [RegexIterator](https://www.php.net/manual/en/class.regexiterator.php) - [RecursiveRegexIterator](https://www.php.net/manual/en/class.recursiveregexiterator.php) - [RecursiveTreeIterator](https://www.php.net/manual/en/class.recursivetreeiterator.php) - **[Random\CryptoSafeEngine](https://www.php.net/manual/en/class.random-cryptosafeengine.php)** (1) [Random\Engine\Secure](https://www.php.net/manual/en/class.random-engine-secure.php) - **[Random\Engine](https://www.php.net/manual/en/class.random-engine.php)** (4) [Random\Engine\Mt19937](https://www.php.net/manual/en/class.random-engine-mt19937.php) - [Random\Engine\PcgOneseq128XslRr64](https://www.php.net/manual/en/class.random-engine-pcgoneseq128xslrr64.php) - [Random\Engine\Xoshiro256StarStar](https://www.php.net/manual/en/class.random-engine-xoshiro256starstar.php) - [Random\Engine\Secure](https://www.php.net/manual/en/class.random-engine-secure.php) - **[RecursiveIterator](https://www.php.net/manual/en/class.recursiveiterator.php)** (13) [RecursiveFilterIterator](https://www.php.net/manual/en/class.recursivefilteriterator.php) - [RecursiveCallbackFilterIterator](https://www.php.net/manual/en/class.recursivecallbackfilteriterator.php) - [ParentIterator](https://www.php.net/manual/en/class.parentiterator.php) - [RecursiveCachingIterator](https://www.php.net/manual/en/class.recursivecachingiterator.php) - [RecursiveRegexIterator](https://www.php.net/manual/en/class.recursiveregexiterator.php) - [RecursiveArrayIterator](https://www.php.net/manual/en/class.recursivearrayiterator.php) - [RecursiveDirectoryIterator](https://www.php.net/manual/en/class.recursivedirectoryiterator.php) - [SplFileObject](https://www.php.net/manual/en/class.splfileobject.php) - [SplTempFileObject](https://www.php.net/manual/en/class.spltempfileobject.php) - [Phar](https://www.php.net/manual/en/class.phar.php) - [PharData](https://www.php.net/manual/en/class.phardata.php) - [SimpleXMLElement](https://www.php.net/manual/en/class.simplexmlelement.php) - [SimpleXMLIterator](https://www.php.net/manual/en/class.simplexmliterator.php) - **[Reflector](https://www.php.net/manual/en/class.reflector.php)** (14) [ReflectionFunctionAbstract](https://www.php.net/manual/en/class.reflectionfunctionabstract.php) - [ReflectionFunction](https://www.php.net/manual/en/class.reflectionfunction.php) - [ReflectionParameter](https://www.php.net/manual/en/class.reflectionparameter.php) - [ReflectionMethod](https://www.php.net/manual/en/class.reflectionmethod.php) - [ReflectionClass](https://www.php.net/manual/en/class.reflectionclass.php) - [ReflectionObject](https://www.php.net/manual/en/class.reflectionobject.php) - [ReflectionProperty](https://www.php.net/manual/en/class.reflectionproperty.php) - [ReflectionClassConstant](https://www.php.net/manual/en/class.reflectionclassconstant.php) - [ReflectionExtension](https://www.php.net/manual/en/class.reflectionextension.php) - [ReflectionZendExtension](https://www.php.net/manual/en/class.reflectionzendextension.php) - [ReflectionAttribute](https://www.php.net/manual/en/class.reflectionattribute.php) - [ReflectionEnum](https://www.php.net/manual/en/class.reflectionenum.php) - [ReflectionEnumUnitCase](https://www.php.net/manual/en/class.reflectionenumunitcase.php) - [ReflectionEnumBackedCase](https://www.php.net/manual/en/class.reflectionenumbackedcase.php) - **[SeekableIterator](https://www.php.net/manual/en/class.seekableiterator.php)** (10) [ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php) - [RecursiveArrayIterator](https://www.php.net/manual/en/class.recursivearrayiterator.php) - [DirectoryIterator](https://www.php.net/manual/en/class.directoryiterator.php) - [FilesystemIterator](https://www.php.net/manual/en/class.filesystemiterator.php) - [RecursiveDirectoryIterator](https://www.php.net/manual/en/class.recursivedirectoryiterator.php) - [GlobIterator](https://www.php.net/manual/en/class.globiterator.php) - [SplFileObject](https://www.php.net/manual/en/class.splfileobject.php) - [SplTempFileObject](https://www.php.net/manual/en/class.spltempfileobject.php) - [Phar](https://www.php.net/manual/en/class.phar.php) - [PharData](https://www.php.net/manual/en/class.phardata.php) - **[Serializable](https://www.php.net/manual/en/class.serializable.php)** (7) [ArrayObject](https://www.php.net/manual/en/class.arrayobject.php) - [ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php) - [RecursiveArrayIterator](https://www.php.net/manual/en/class.recursivearrayiterator.php) - [SplDoublyLinkedList](https://www.php.net/manual/en/class.spldoublylinkedlist.php) - [SplQueue](https://www.php.net/manual/en/class.splqueue.php) - [SplStack](https://www.php.net/manual/en/class.splstack.php) - [SplObjectStorage](https://www.php.net/manual/en/class.splobjectstorage.php) - **[SessionHandlerInterface](https://www.php.net/manual/en/class.sessionhandlerinterface.php)** (1) [SessionHandler](https://www.php.net/manual/en/class.sessionhandler.php) - **[SessionIdInterface](https://www.php.net/manual/en/class.sessionidinterface.php)** (1) [SessionHandler](https://www.php.net/manual/en/class.sessionhandler.php) - **[Stringable](https://www.php.net/manual/en/class.stringable.php)** (84) [Exception](https://www.php.net/manual/en/class.exception.php) - [ErrorException](https://www.php.net/manual/en/class.errorexception.php) - [Error](https://www.php.net/manual/en/class.error.php) - [CompileError](https://www.php.net/manual/en/class.compileerror.php) - [ParseError](https://www.php.net/manual/en/class.parseerror.php) - [TypeError](https://www.php.net/manual/en/class.typeerror.php) - [ArgumentCountError](https://www.php.net/manual/en/class.argumentcounterror.php) - [ValueError](https://www.php.net/manual/en/class.valueerror.php) - [ArithmeticError](https://www.php.net/manual/en/class.arithmeticerror.php) - [DivisionByZeroError](https://www.php.net/manual/en/class.divisionbyzeroerror.php) - [UnhandledMatchError](https://www.php.net/manual/en/class.unhandledmatcherror.php) - [ClosedGeneratorException](https://www.php.net/manual/en/class.closedgeneratorexception.php) - [FiberError](https://www.php.net/manual/en/class.fibererror.php) - [DateError](https://www.php.net/manual/en/class.dateerror.php) - [DateObjectError](https://www.php.net/manual/en/class.dateobjecterror.php) - [DateRangeError](https://www.php.net/manual/en/class.daterangeerror.php) - [DateException](https://www.php.net/manual/en/class.dateexception.php) - [DateInvalidTimeZoneException](https://www.php.net/manual/en/class.dateinvalidtimezoneexception.php) - [DateInvalidOperationException](https://www.php.net/manual/en/class.dateinvalidoperationexception.php) - [DateMalformedStringException](https://www.php.net/manual/en/class.datemalformedstringexception.php) - [DateMalformedIntervalStringException](https://www.php.net/manual/en/class.datemalformedintervalstringexception.php) - [DateMalformedPeriodStringException](https://www.php.net/manual/en/class.datemalformedperiodstringexception.php) - [SQLite3Exception](https://www.php.net/manual/en/class.sqlite3exception.php) - [DOMException](https://www.php.net/manual/en/class.domexception.php) - [FFI\Exception](https://www.php.net/manual/en/class.ffi-exception.php) - [FFI\ParserException](https://www.php.net/manual/en/class.ffi-parserexception.php) - [JsonException](https://www.php.net/manual/en/class.jsonexception.php) - [IntlException](https://www.php.net/manual/en/class.intlexception.php) - [LogicException](https://www.php.net/manual/en/class.logicexception.php) - [BadFunctionCallException](https://www.php.net/manual/en/class.badfunctioncallexception.php) - [BadMethodCallException](https://www.php.net/manual/en/class.badmethodcallexception.php) - [DomainException](https://www.php.net/manual/en/class.domainexception.php) - [InvalidArgumentException](https://www.php.net/manual/en/class.invalidargumentexception.php) - [LengthException](https://www.php.net/manual/en/class.lengthexception.php) - [OutOfRangeException](https://www.php.net/manual/en/class.outofrangeexception.php) - [RuntimeException](https://www.php.net/manual/en/class.runtimeexception.php) - [OutOfBoundsException](https://www.php.net/manual/en/class.outofboundsexception.php) - [OverflowException](https://www.php.net/manual/en/class.overflowexception.php) - [RangeException](https://www.php.net/manual/en/class.rangeexception.php) - [UnderflowException](https://www.php.net/manual/en/class.underflowexception.php) - [UnexpectedValueException](https://www.php.net/manual/en/class.unexpectedvalueexception.php) - [CachingIterator](https://www.php.net/manual/en/class.cachingiterator.php) - [RecursiveCachingIterator](https://www.php.net/manual/en/class.recursivecachingiterator.php) - [SplFileInfo](https://www.php.net/manual/en/class.splfileinfo.php) - [DirectoryIterator](https://www.php.net/manual/en/class.directoryiterator.php) - [FilesystemIterator](https://www.php.net/manual/en/class.filesystemiterator.php) - [RecursiveDirectoryIterator](https://www.php.net/manual/en/class.recursivedirectoryiterator.php) - [GlobIterator](https://www.php.net/manual/en/class.globiterator.php) - [SplFileObject](https://www.php.net/manual/en/class.splfileobject.php) - [SplTempFileObject](https://www.php.net/manual/en/class.spltempfileobject.php) - [AssertionError](https://www.php.net/manual/en/class.assertionerror.php) - [PDOException](https://www.php.net/manual/en/class.pdoexception.php) - [PharException](https://www.php.net/manual/en/class.pharexception.php) - [Phar](https://www.php.net/manual/en/class.phar.php) - [PharData](https://www.php.net/manual/en/class.phardata.php) - [PharFileInfo](https://www.php.net/manual/en/class.pharfileinfo.php) - [Random\RandomError](https://www.php.net/manual/en/class.random-randomerror.php) - [Random\BrokenRandomEngineError](https://www.php.net/manual/en/class.random-brokenrandomengineerror.php) - [Random\RandomException](https://www.php.net/manual/en/class.random-randomexception.php) - [ReflectionException](https://www.php.net/manual/en/class.reflectionexception.php) - [ReflectionFunctionAbstract](https://www.php.net/manual/en/class.reflectionfunctionabstract.php) - [ReflectionFunction](https://www.php.net/manual/en/class.reflectionfunction.php) - [ReflectionParameter](https://www.php.net/manual/en/class.reflectionparameter.php) - [ReflectionType](https://www.php.net/manual/en/class.reflectiontype.php) - [ReflectionNamedType](https://www.php.net/manual/en/class.reflectionnamedtype.php) - [ReflectionUnionType](https://www.php.net/manual/en/class.reflectionuniontype.php) - [ReflectionIntersectionType](https://www.php.net/manual/en/class.reflectionintersectiontype.php) - [ReflectionMethod](https://www.php.net/manual/en/class.reflectionmethod.php) - [ReflectionClass](https://www.php.net/manual/en/class.reflectionclass.php) - [ReflectionObject](https://www.php.net/manual/en/class.reflectionobject.php) - [ReflectionProperty](https://www.php.net/manual/en/class.reflectionproperty.php) - [ReflectionClassConstant](https://www.php.net/manual/en/class.reflectionclassconstant.php) - [ReflectionExtension](https://www.php.net/manual/en/class.reflectionextension.php) - [ReflectionZendExtension](https://www.php.net/manual/en/class.reflectionzendextension.php) - [ReflectionAttribute](https://www.php.net/manual/en/class.reflectionattribute.php) - [ReflectionEnum](https://www.php.net/manual/en/class.reflectionenum.php) - [ReflectionEnumUnitCase](https://www.php.net/manual/en/class.reflectionenumunitcase.php) - [ReflectionEnumBackedCase](https://www.php.net/manual/en/class.reflectionenumbackedcase.php) - [mysqli_sql_exception](https://www.php.net/manual/en/class.mysqli-sql-exception.php) - [SimpleXMLElement](https://www.php.net/manual/en/class.simplexmlelement.php) - [SimpleXMLIterator](https://www.php.net/manual/en/class.simplexmliterator.php) - [SoapFault](https://www.php.net/manual/en/class.soapfault.php) - [SodiumException](https://www.php.net/manual/en/class.sodiumexception.php) - [PhpToken](https://www.php.net/manual/en/class.phptoken.php) - **[Throwable](https://www.php.net/manual/en/class.throwable.php)** (51) [Exception](https://www.php.net/manual/en/class.exception.php) - [ErrorException](https://www.php.net/manual/en/class.errorexception.php) - [Error](https://www.php.net/manual/en/class.error.php) - [CompileError](https://www.php.net/manual/en/class.compileerror.php) - [ParseError](https://www.php.net/manual/en/class.parseerror.php) - [TypeError](https://www.php.net/manual/en/class.typeerror.php) - [ArgumentCountError](https://www.php.net/manual/en/class.argumentcounterror.php) - [ValueError](https://www.php.net/manual/en/class.valueerror.php) - [ArithmeticError](https://www.php.net/manual/en/class.arithmeticerror.php) - [DivisionByZeroError](https://www.php.net/manual/en/class.divisionbyzeroerror.php) - [UnhandledMatchError](https://www.php.net/manual/en/class.unhandledmatcherror.php) - [ClosedGeneratorException](https://www.php.net/manual/en/class.closedgeneratorexception.php) - [FiberError](https://www.php.net/manual/en/class.fibererror.php) - [DateError](https://www.php.net/manual/en/class.dateerror.php) - [DateObjectError](https://www.php.net/manual/en/class.dateobjecterror.php) - [DateRangeError](https://www.php.net/manual/en/class.daterangeerror.php) - [DateException](https://www.php.net/manual/en/class.dateexception.php) - [DateInvalidTimeZoneException](https://www.php.net/manual/en/class.dateinvalidtimezoneexception.php) - [DateInvalidOperationException](https://www.php.net/manual/en/class.dateinvalidoperationexception.php) - [DateMalformedStringException](https://www.php.net/manual/en/class.datemalformedstringexception.php) - [DateMalformedIntervalStringException](https://www.php.net/manual/en/class.datemalformedintervalstringexception.php) - [DateMalformedPeriodStringException](https://www.php.net/manual/en/class.datemalformedperiodstringexception.php) - [SQLite3Exception](https://www.php.net/manual/en/class.sqlite3exception.php) - [DOMException](https://www.php.net/manual/en/class.domexception.php) - [FFI\Exception](https://www.php.net/manual/en/class.ffi-exception.php) - [FFI\ParserException](https://www.php.net/manual/en/class.ffi-parserexception.php) - [JsonException](https://www.php.net/manual/en/class.jsonexception.php) - [IntlException](https://www.php.net/manual/en/class.intlexception.php) - [LogicException](https://www.php.net/manual/en/class.logicexception.php) - [BadFunctionCallException](https://www.php.net/manual/en/class.badfunctioncallexception.php) - [BadMethodCallException](https://www.php.net/manual/en/class.badmethodcallexception.php) - [DomainException](https://www.php.net/manual/en/class.domainexception.php) - [InvalidArgumentException](https://www.php.net/manual/en/class.invalidargumentexception.php) - [LengthException](https://www.php.net/manual/en/class.lengthexception.php) - [OutOfRangeException](https://www.php.net/manual/en/class.outofrangeexception.php) - [RuntimeException](https://www.php.net/manual/en/class.runtimeexception.php) - [OutOfBoundsException](https://www.php.net/manual/en/class.outofboundsexception.php) - [OverflowException](https://www.php.net/manual/en/class.overflowexception.php) - [RangeException](https://www.php.net/manual/en/class.rangeexception.php) - [UnderflowException](https://www.php.net/manual/en/class.underflowexception.php) - [UnexpectedValueException](https://www.php.net/manual/en/class.unexpectedvalueexception.php) - [AssertionError](https://www.php.net/manual/en/class.assertionerror.php) - [PDOException](https://www.php.net/manual/en/class.pdoexception.php) - [PharException](https://www.php.net/manual/en/class.pharexception.php) - [Random\RandomError](https://www.php.net/manual/en/class.random-randomerror.php) - [Random\BrokenRandomEngineError](https://www.php.net/manual/en/class.random-brokenrandomengineerror.php) - [Random\RandomException](https://www.php.net/manual/en/class.random-randomexception.php) - [ReflectionException](https://www.php.net/manual/en/class.reflectionexception.php) - [mysqli_sql_exception](https://www.php.net/manual/en/class.mysqli-sql-exception.php) - [SoapFault](https://www.php.net/manual/en/class.soapfault.php) - [SodiumException](https://www.php.net/manual/en/class.sodiumexception.php) - **[Traversable](https://www.php.net/manual/en/class.traversable.php)** (54) [InternalIterator](https://www.php.net/manual/en/class.internaliterator.php) - [Generator](https://www.php.net/manual/en/class.generator.php) - [WeakMap](https://www.php.net/manual/en/class.weakmap.php) - [DatePeriod](https://www.php.net/manual/en/class.dateperiod.php) - [DOMNodeList](https://www.php.net/manual/en/class.domnodelist.php) - [DOMNamedNodeMap](https://www.php.net/manual/en/class.domnamednodemap.php) - [ResourceBundle](https://www.php.net/manual/en/class.resourcebundle.php) - [IntlIterator](https://www.php.net/manual/en/class.intliterator.php) - [IntlBreakIterator](https://www.php.net/manual/en/class.intlbreakiterator.php) - [IntlRuleBasedBreakIterator](https://www.php.net/manual/en/class.intlrulebasedbreakiterator.php) - [IntlCodePointBreakIterator](https://www.php.net/manual/en/class.intlcodepointbreakiterator.php) - [IntlPartsIterator](https://www.php.net/manual/en/class.intlpartsiterator.php) - [RecursiveIteratorIterator](https://www.php.net/manual/en/class.recursiveiteratoriterator.php) - [IteratorIterator](https://www.php.net/manual/en/class.iteratoriterator.php) - [FilterIterator](https://www.php.net/manual/en/class.filteriterator.php) - [RecursiveFilterIterator](https://www.php.net/manual/en/class.recursivefilteriterator.php) - [CallbackFilterIterator](https://www.php.net/manual/en/class.callbackfilteriterator.php) - [RecursiveCallbackFilterIterator](https://www.php.net/manual/en/class.recursivecallbackfilteriterator.php) - [ParentIterator](https://www.php.net/manual/en/class.parentiterator.php) - [LimitIterator](https://www.php.net/manual/en/class.limititerator.php) - [CachingIterator](https://www.php.net/manual/en/class.cachingiterator.php) - [RecursiveCachingIterator](https://www.php.net/manual/en/class.recursivecachingiterator.php) - [NoRewindIterator](https://www.php.net/manual/en/class.norewinditerator.php) - [AppendIterator](https://www.php.net/manual/en/class.appenditerator.php) - [InfiniteIterator](https://www.php.net/manual/en/class.infiniteiterator.php) - [RegexIterator](https://www.php.net/manual/en/class.regexiterator.php) - [RecursiveRegexIterator](https://www.php.net/manual/en/class.recursiveregexiterator.php) - [EmptyIterator](https://www.php.net/manual/en/class.emptyiterator.php) - [RecursiveTreeIterator](https://www.php.net/manual/en/class.recursivetreeiterator.php) - [ArrayObject](https://www.php.net/manual/en/class.arrayobject.php) - [ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php) - [RecursiveArrayIterator](https://www.php.net/manual/en/class.recursivearrayiterator.php) - [DirectoryIterator](https://www.php.net/manual/en/class.directoryiterator.php) - [FilesystemIterator](https://www.php.net/manual/en/class.filesystemiterator.php) - [RecursiveDirectoryIterator](https://www.php.net/manual/en/class.recursivedirectoryiterator.php) - [GlobIterator](https://www.php.net/manual/en/class.globiterator.php) - [SplFileObject](https://www.php.net/manual/en/class.splfileobject.php) - [SplTempFileObject](https://www.php.net/manual/en/class.spltempfileobject.php) - [SplDoublyLinkedList](https://www.php.net/manual/en/class.spldoublylinkedlist.php) - [SplQueue](https://www.php.net/manual/en/class.splqueue.php) - [SplStack](https://www.php.net/manual/en/class.splstack.php) - [SplHeap](https://www.php.net/manual/en/class.splheap.php) - [SplMinHeap](https://www.php.net/manual/en/class.splminheap.php) - [SplMaxHeap](https://www.php.net/manual/en/class.splmaxheap.php) - [SplPriorityQueue](https://www.php.net/manual/en/class.splpriorityqueue.php) - [SplFixedArray](https://www.php.net/manual/en/class.splfixedarray.php) - [SplObjectStorage](https://www.php.net/manual/en/class.splobjectstorage.php) - [MultipleIterator](https://www.php.net/manual/en/class.multipleiterator.php) - [PDOStatement](https://www.php.net/manual/en/class.pdostatement.php) - [Phar](https://www.php.net/manual/en/class.phar.php) - [PharData](https://www.php.net/manual/en/class.phardata.php) - [mysqli_result](https://www.php.net/manual/en/class.mysqli-result.php) - [SimpleXMLElement](https://www.php.net/manual/en/class.simplexmlelement.php) - [SimpleXMLIterator](https://www.php.net/manual/en/class.simplexmliterator.php) - **[UnitEnum](https://www.php.net/manual/en/class.unitenum.php)** (1) [Random\IntervalBoundary](https://www.php.net/manual/en/enum.random.intervalboundary.php) - **Interfaces without implementation** (4) [BackedEnum](https://www.php.net/manual/en/class.backedenum.php) - [SplObserver](https://www.php.net/manual/en/class.splobserver.php) - [SplSubject](https://www.php.net/manual/en/class.splsubject.php) - [SessionUpdateTimestampHandlerInterface](https://www.php.net/manual/en/class.sessionupdatetimestamphandlerinterface.php) --- # Common services and tools for PHP applications Source: https://www.exakat.io/common-services-with-php-application/ ## Common services and tools for PHP applications PHP applications all have *.php files. Then, there are other files, needed for configuration, templating, or also external services. These files gives a measure of the popularity of the common services with PHP applications. The most famous is certainly composer, which is now present in virtually every PHP applications: over 3100 projects, we counted a little more than 170 that are not displaying a 'composer.json' file. Then, you can count on git, which is also present in a lot of other projects. After these two darlings of the PHP community, there are a long list of services, online or installable, that leave one or more configuration files. Most of us recognize a .htaccess, .idea, or a package.json. Some services are quite popular, and that is reflected in the configuration files.   Here is the list of the common services with PHP application, as found in 3100 PHP open source projects.   | Name | Files | Count | | ---- | ----- | ----- | | [Composer](https://getcomposer.org/) | composer.json, composer.lock, vendor, composer.phar | 2973 | | [git](https://git-scm.com/) | .git, .gitignore, .gitattributes, .gitmodules, .mailmap, .githooks, .git-hooks, .git-blame-ignore-revs | 2322 | | A readme file | README.md, readme.md, README, readme.rst, readme.txt | 2085 | | A license file | LICENSE, license.md, LICENSE.md, license.txt, LICENSE.txt, license.rst | 1606 | | [PHPUnit](https://www.phpunit.de/) | phpunit.xml.dist, phpunit.xml, phpunit.xml.legacy, phpunit.dist.xml, phpunit-unit.xml | 987 | | [github](https://www.github.com/) | .github | 890 | | [editor config](https://editorconfig.org/) | .editorconfig | 577 | | [Travis](https://travis-ci.org/) | .travis.yml, .env.travis, .travis, .travis.php.ini, .travis.coverage.sh, .travis.ini, travis.php.ini, .travis.install.sh | 390 | | [Code Sniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer) | .php_cs, .php_cs.dist, .phpcs.xml, php_cs.dist, phpcs.xml, phpcs.xml.dist, ruleset.xml, .phpcs.xml.dist | 378 | | [PHPStan](https://github.com/phpstan) | phpstan.neon, .phpstan.neon, phpstan.neon.dist, phpstan-baseline.neon, phpstan.tests.neon.dist, phpstan.dist.neon | 365 | | [npm](https://www.npmjs.com/) | package.json, .npmignore, .npmrc, package-lock.json | 240 | | [Docker](http://www.docker.com/) | .dockerignore, .docker, docker-compose.yml, docker-compose.yaml, Dockerfile, .env.docker | 216 | | [PHP-CS-Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) | .php-cs-fixer.php, .php-cs-fixer.dist.php | 203 | | [Makefile](https://www.gnu.org/software/make/manual/make.html) | Makefile | 152 | | [dotenv](https://www.dotenv.org/docs/) | .env.dist, .env, .env.example | 144 | | [Psalm](https://getpsalm.org/) | psalm.xml, psalm-baseline.xml, psalm.xml.dist | 142 | | [scrutinizer](https://scrutinizer-ci.com/) | .scrutinizer.yml | 132 | | [Apache](http://www.apache.org/) | .htaccess, htaccess.txt | 118 | | [Drone](http://docs.drone.io/) | .dockerignore, .docker | 87 | | [Webpack](https://webpack.js.org/) | webpack.mix.js, webpack.config.js, webpack.ssr.mix.js | 86 | | [StyleCI](https://styleci.io/) | .styleci.yml | 76 | | [artisan](http://laravel.com/docs/5.1/artisan) | artisan | 73 | | [rector](https://github.com/rectorphp/rector) | rector.php | 73 | | [Apple](http://www.apple.com/) | .DS_Store | 64 | | [esLint](http://eslint.org/) | .eslintrc, .eslintignore, eslintrc.js, .eslintrc.js, .eslintrc.json | 64 | | [infection](https://infection.github.io/) | infection.yml, .infection.yml, infection.json.dist, infection.json | 56 | | [Yarn](https://yarnpkg.com/lang/en/) | yarn.lock, .yarnclean | 55 | | [codecov](https://codecov.io/) | .codecov.yml, codecov.yml | 53 | | [VisualStudio](https://code.visualstudio.com/) | .vscode, .devcontainer | 48 | | [PHPMD](https://phpmd.org/) | phpmd.xml, phpmd.xml.dist, phpmd_ruleset.xml | 47 | | [robots.txt](http://www.robotstxt.org/) | robots.txt | 47 | | [favicon](https://en.wikipedia.org/wiki/Favicon) | favicon.ico | 46 | | [gitlab](https://www.gitlab.com/) | .gitlab-ci.yml | 45 | | [jetBrains](https://www.jetbrains.com/phpstorm/) | .idea | 41 | | [Prettier](https://prettier.io/) | .prettierrc, .prettierignore, .prettierrc.json, .prettierrc.js | 41 | | [TailWind](https://tailwindcss.com/) | tailwind.config.js, tailwind.js | 41 | | [BabelJS](https://babeljs.io/) | .babel.rc, .babel.js, .babelrc, babel.config.js | 39 | | [Coveralls](https://github.com/php-coveralls/php-coveralls) | .coveralls.yml | 36 | | [GrumPHP](https://github.com/phpro/grumphp) | grumphp.yml.dist, grumphp.yml, grumphp.dist.yml | 36 | | [codeClimate](http://www.codeclimate.com/) | .codeclimate.yml | 35 | | [box2](https://github.com/box-project/box2) | box.json, box.json.dist | 32 | | [circleCI](https://circleci.com/) | circle.yml, .circleci | 31 | | [PostCSS](https://github.com/postcss/postcss) | postcss.config.js | 31 | | [codeception](https://codeception.com/) | codeception.yml, codeception.dist.yml | 30 | | [Typescript](https://www.typescriptlang.org/) | tsconfig.json | 30 | | [Appveyor](http://www.appveyor.com/) | appveyor.yml, .appveyor.yml | 29 | | [ant](https://ant.apache.org/) | build.xml | 29 | | [PHPBench](https://github.com/phpbench/phpbench) | phpbench.json, phpbench.json.dist | 28 | | [Laravel Pint](https://laravel.com/docs/10.x/pint) | pint.json | 28 | | [phpspec](https://phpspec.net/en/stable/) | phpspec.yml, .phpspec, phpspec.yml.dist | 28 | | [Stylelint](https://stylelint.io/) | .stylelintrc, .stylelintignore, .stylelintrc.json, stylelint.config.js | 28 | | [vite](https://vitejs.dev/) | vite.config.js | 28 | | [Node Version Manager](https://github.com/nvm-sh/nvm) | .nvmrc | 27 | | [easy coding standards](https://github.com/easy-coding-standard/easy-coding-standard) | ecs.php | 26 | | [MkDocs](http://www.mkdocs.org) | mkdocs.yml | 26 | | [PhpStorm](https://www.jetbrains.com/phpstorm/) | .phpstorm.meta.php | 26 | | [SonarCube](https://www.sonarsource.com/) | sonar-project.properties | 26 | | [PHPDoc](https://www.phpdoc.org/) | .phpdoc.xml, phpdoc.dist.xml, phpdoc.xml.dist | 25 | | [Gulp](http://gulpjs.com/) | gulpfile.js, gulpfile.babel.js | 24 | | [Phive](https://phar.io/) | phive.xml, .phive | 23 | | [Crowdin](https://crowdin.com/) | crowdin.yml | 22 | | [GruntJS](https://gruntjs.com/) | Gruntfile.js, gruntfile.js | 20 | | [Behat](http://docs.behat.org/en/v2.5/) | behat.yml.dist, behat.yml | 19 | | [renovate](https://www.renovatebot.com/) | renovate.json | 19 | | [Vagrant](https://www.vagrantup.com/) | Vagrantfile | 19 | | [jshint](http://jshint.com/) | .jshintrc, .jshintignore | 18 | | [Read the Docs](https://about.readthedocs.com/) | .readthedocs.yml, .readthedocs.yaml | 17 | | [Symfony](https://symfony.com/) | symfony.lock | 17 | | [Jekyll](https://jekyllrb.com/) | _config.yml, _config.toml | 16 | | [phan](https://github.com/etsy/phan) | .phan | 15 | | [Composer Require Checker](https://github.com/maglnet/ComposerRequireChecker) | composer-require-checker.json | 14 | | [transifex](https://www.transifex.com/) | .tx | 14 | | [Browserslist](https://github.com/browserslist/browserslist) | .browserslistrc | 13 | | [ASP.net](https://dotnet.microsoft.com/en-us/apps/aspnet) | web.config | 12 | | [husky](https://typicode.github.io/husky/) | .husky | 11 | | [phplint](https://github.com/overtrue/phplint) | .phplint.yml | 11 | | [robo](https://robo.li/) | RoboFile.php, robo.yml.dist | 11 | | [captainhook](https://github.com/captainhookphp/captainhook) | captainhook.json | 10 | | [Lando dev](https://lando.dev/) | .lando.yml | 9 | | [PHPDox](https://github.com/theseer/phpdox) | phpdox.xml.dist, phpdox.xml | 9 | | [bitbucket](https://bitbucket.org/product) | bitbucket-pipelines.yml, bitbucket-pipelines.yml.template, bitbucket_packagist_scripts.json | 8 | | [Bower](http://bower.io/) | bower.json, .bowerrc | 8 | | [cypress](https://www.cypress.io/) | cypress.config.js, cypress.config.ts | 8 | | [direnv](https://direnv.net/) | .envrc | 8 | | [DocHeader](https://github.com/malukenho/docheader) | .docheader | 8 | | [gitpod](https://www.gitpod.io/) | .gitpod.yml, gitpod.code-workspace, .gitpod.dockerfile, .gitpod.Dockerfile | 8 | | [Ahoy](https://github.com/ahoy-cli) | ahoy.yml, .ahoy.l3d.yml | 7 | | [Jest](https://jestjs.io/) | jest.config.js | 7 | | [SVN](https://subversion.apache.org/) | svn.revision, .svn, .svnignore | 7 | | [apigen](http://apigen.github.io/ApiGen/) | apigen.yml, apigen.neon | 6 | | [Arcanist](https://secure.phabricator.com/book/phabricator/article/arcanist_lint/) | .arclint, .arcconfig | 6 | | [Laravel Mix](https://laravel-mix.com/docs/6.0/versioning) | mix-manifest.json | 6 | | [doxygen](https://www.doxygen.nl/index.html) | Doxyfile | 5 | | [Flakes](https://nixos.wiki/wiki/Flakes) | flake.lock, flake.nix | 5 | | [FlintCI](https://flintci.io/) | .flintci.yml | 5 | | [Insight](https://insight.symfony.com/) | .sensiolabs.yml, .symfony.insight.yaml | 5 | | [Atoum](http://atoum.org/) | .bootstrap.atoum.php, .atoum.php, .atoum.bootstrap.php | 4 | | [gitbook](https://www.gitbook.com/) | .gitbook.yaml | 4 | | [pdepend](https://github.com/pdepend/pdepend) | pdepend.xml, pdepend.xml.dist | 4 | | [stickler](https://stickler-ci.com/docs) | .stickler.yml | 4 | | [Vue](https://vuejs.org/) | vue.config.js | 4 | | [Couscous](http://couscous.io/) | couscous.yml | 3 | | [deptrack](https://github.com/qossmic/deptrac) | deptrac.yaml | 3 | | [Drush](https://www.drush.org/) | drush.services.yml | 3 | | [humbug](https://github.com/humbug/box.git) | humbug.json.dist, humbug.json | 3 | | [Karma](https://karma-runner.github.io/latest/index.html) | ./karma.conf.js, ./karma.conf.coffee, ./karma.conf.ts, karma.conf.js | 3 | | [lerna](https://lerna.js.org/) | lerna.json | 3 | | [Snyk](https://snyk.io/) | .snyk | 3 | | [Sublime Linter](http://www.sublimelinter.com/en/latest/) | .csslintrc | 3 | | [Docblox](https://github.com/dzuelke/Docblox.git) | docblox.dist.xml | 2 | | [Exakat](https://www.exakat.io/) | .exakat.yaml, .exakat.yml, .exakat.ini | 2 | | [Garden.io](https://garden.io/) | garden.yaml | 2 | | [gherkin](https://cucumber.io/docs/gherkin/) | .gherkin-lintrc | 2 | | [Red Hat OpenShift](https://www.openshift.com/) | .openshift | 2 | | [Qodana](https://www.jetbrains.com/qodana/) | qodana.yaml | 2 | | [yamllint](https://github.com/adrienverge/yamllint) | .yamllint.yaml | 2 | | [Ansistrano](https://ansistrano.com/) | .ansistrano | 1 | | [Mercurial](https://www.mercurial-scm.org/) | .hg, .hgtags, .hgignore, .hgeol | 1 | | [PHPCI](https://www.phptesting.org/) | phpci.yml | 1 | | [semantic versioning](http://semver.org/) | .semver | 1 | | [shifter](https://getshifter.io/) | .shifter.json | 1 |   ## Analysis description The survey was conducted on a list of 3100 PHP Open Source projects. That include the 1000 most common packagist projects, the trendy ones on github, and a collection of various less known projects. The first and second level of files were collected, attributed to a service, Saas or installable, and counted. Some services don't have any configuration, or these files are not in the first two levels of the filesystem: there were not counted. Some services may have been missed. Please, tell us on [Mastodon](https://phpc.social/@Exakat).   --- # PHP Constructors and Inheritance Source: https://www.exakat.io/php-constructors-and-inheritance/ ## PHP Constructors and Inheritance When working in OOP PHP, understanding constructors is essential for manipulating objects. Constructors are magic methods that get invoked when an object is instantiated from a class. They typically handle the initialization of an object’s properties. These initialisations are split between parent and child classes, and the relationship between parent and child class constructors can be somewhat confusing. There is much to say about PHP constructors and inheritance. In this post, we’ll explore PHP constructors in the context of inheritance and what it means when a child constructor explicitly calls the parent constructor. ## What Is a Constructor in PHP? In PHP, a constructor is a magic function that is automatically executed when a new instance of a class is created. It is used to initialize the object's properties or perform setup tasks that are required at the time of object creation. The constructor in PHP is defined with the __construct method. Here’s a basic example: In this example, when the new object X is instantiated, PHP automatically invokes the `__construct()` method to initialize the properties of the car object. ## What Happens with Inheritance? When one class extends another in PHP, the child class inherits all public and protected methods from the parent class, including the constructor. However, parent constructors in PHP do not get called automatically, but in one case: when the child class doesn’t define a constructor, PHP calls the parent constructor automatically. This is where things get interesting: when the child class defines its own constructor, it does not automatically inherit the parent constructor. Instead, the child class has full control over the construction process, and the parent constructor will only be called if the child explicitly calls it. In this example, the `Y` class does not call call the parent class constructor, and only the local constructor is called. The parent class is not initialized at all, in particular the property `$property`. To do so, the child constructor must call the parent constructor explicitly. Heuristically, the parent must be called first, so as to ensure all needed is set up, before the child object can use specific features. Yet, when the parent and the child are well decoupled, that doesn't impact much the constructor. ## Fatal Error: Cannot Call Constructor From that situation, it is easy to conclude that calling the parent constructor is a duty for the child: even if it has no impact, it is safer to relay the call to the parent. But PHP now needs a concrete method to call, and constructors are not compulsory. In this scenario, PHP throws a fatal error: `Cannot call constructor`. This is quite confusing, at first, since there is actually a constructor in `Y`. This happens because the Y class defines a constructor, but the parent class `X` doesn’t. When PHP tries to find the parent constructor, it doesn't exist. That problem is not the case when there is a grand-parent class, with an explicit constructor. Any existing parent constructor will prevent the Fatal error. ## Must call the parent constructor Interestingly, some PHP native classes exhibit a behavior where they throw a LogicException if a child class doesn’t explicitly call the parent constructor. Consider the [SplFileObject](https://www.php.net/manual/en/class.splfileobject.php) class, which is used to provide a OOP syntax to files. When extended, it requires a proper setup of the file path by the parent class. When `SplFileObject` is extended and lacks the call to the parent constructor, a `LogicException` is thrown at execution time: This native behavior ensures that the necessary setup is always performed correctly. ## The Difficulty of Managing Parent Constructors While PHP’s constructor inheritance model is straightforward, it introduces subtle problems in more complex scenarios. One of the primary issues is determining whether the parent class even has a constructor to call. It is difficult to setup a coding convention without prior knowledge of the parent hierarchy (the family?): - always call the parent constructor, and end up coding [empty constructors](https://exakat.readthedocs.io/en/latest/Reference/Rules/Functions/EmptyFunction.html) - only call the parent constuctor when needed, and forget one that [need it](https://exakat.readthedocs.io/en/latest/Reference/Rules/Php/MustCallParentConstructor.html) Static code analysis may help with that. Furthermore, when dealing with frameworks where there is no full control over the parent class, for example, extending a third-party class, it may be hard to know when to call the parent constructor. This introduces a certain level of uncertainty into the design, potentially leading to bugs that are identified at execution time only. And, finally, not calling the parent is also a valid strategy when creating mocks or unplugging existing code. Cases are rare to short-circuit the parent constructor, but do exist. ## A Potential Solution: Default Empty Constructor An idea to alleviate this issue would be to have PHP provide an empty constructor by default, similar to how it works with properties. In cases where a child class explicitly calls a parent class that does not define a constructor, PHP could automatically provide an empty constructor that does nothing. With inheritance, the parent __construct() may be in a grand-parent, though it is already handled that way today. This way, a child class could still call parent::__construct() without worrying about whether or not the parent class has a constructor. Here’s an illustration: In this case, PHP would simply use an empty constructor in `X` if it wasn’t explicitly defined, thus preventing errors and simplifying the inheritance model. ## Conclusion PHP's handling of constructors in the context of inheritance can be tricky. When a child class overrides a parent constructor, it must explicitly call the parent’s constructor if any. Otherwise, a fatal error or unexpected behavior may arise. Additionally, some PHP classes enforce constructor calls through exceptions, adding another layer of complexity. In more complex applications or frameworks, the uncertainty of whether to call a parent constructor—especially when dealing with third-party code—can introduce extra code burden. A default empty constructor mechanism could potentially make this process smoother, but for now, developers need to be diligent in understanding when and why to invoke a parent constructor. --- # Bitwise and Logical Operators in PHP Source: https://www.exakat.io/bitwise-and-logical-operators-in-php/ ## Difference Between Bitwise and Logical Operators in PHP The distinction between [bitwise operators](https://www.php.net/manual/en/language.operators.bitwise.php) and [logical operators](https://www.php.net/manual/en/language.operators.logical.php) operators is often overlooked and confused. They are often used one for another: it is true they have some overlapping domains of application, and yet, their functionality differ significantly. Here’s a breakdown of how these operators work: ### Logical Operators: Booleans at Play Logical operators in PHP operate on boolean values and return boolean results. For example: These operators are often represented using integers, specifically 0 and 1, where 0 is equivalent to false and 1 is equivalent to true. Consider the following example: Under the hood, PHP casts any value to a boolean before processing it with a logical operator. This ensures that logical operations are consistently boolean-focused. ### Bitwise Operators: Working with Integers Bitwise operators, on the other hand, operate directly on integers. They treat integers as binary representations and perform logical operations, bit by bit. Hence, the name of bitwise operators. For instance: Here’s what happens, in binary notation, in the second example: ``` 101 (5 in binary) & 011 (3 in binary) --- 001 (1 in binary) ``` Each bit in the integer is evaluated using the corresponding logical operation, and the result is placed in the corresponding position of the resulting integer. Finally, this result is returned as an integer. ### The Systemic Perspective From a broader perspective, you can think of bitwise operators as logical operators applied to multiple values simultaneously. For example, an 8-bit integer could be viewed as eight boolean values, with bitwise operators processing all bits in parallel. ### Overlap Between Bitwise and Logical Operators While logical and bitwise operators serve different purposes, there is some functional overlap. Notably: - `!$a`: Negates the value of `$a`. For example, if `$a` is true or 0, `!$a` is false. - `~$a`: Negates all the bits in $a. For instance, if `$a` is 0, `~$a` is -1, and not 1, although both are loosely equal to true. if `$a` is 1, `~$a` is -2, and not false. Then, their behavior diverges when $a is not strictly 0 or 1, making it essential to understand their respective contexts. ## Conclusion [Logical operators](https://www.freecodecamp.org/news/logical-operators-in-php/) are works well for boolean logic, in particular for any true/false conditions. [Bitwise operators](https://flatcoding.com/tutorials/php-programming/understanding-the-php-bitwise-operators/), on the other hand, offer a powerful way to manipulate integers at the binary level, making them invaluable for tasks that involve low-level data processing. --- # PHP likes sorting too much Source: https://www.exakat.io/php-likes-sorting/ PHP likes to sort. Of course, there is sort(), ksort() and all the cousins. But, PHP actually sorts too much. My first encounter with the problem is the infamous array_unique(). Now, this is also affecting glob() and scandir(). I'm looking for others. Until then, check your code. ## array_unique() is also sorting [array_unique()](http://www.php.net/array_unique) collects distinct values from an array. However, its performances degrades quite quickly with the size of the array. This is quite strange : with 100 elements, array_unique() is 20 times slower than array_keys/array_count_values, and with 1000 elements, it is actually 130 times slower. From the manual, one may realize that array_merge() does some sorting. The 2nd argument is indeed an option to change the sorting in array_merge(). The irony is that the resulting array in never sorted in anyway. ## Glob() and scandir() are sorting Other functions that sorts too much are [glob](http://www.php.net/glob)() and [scandir](http://www.php.net/scandir)(). Glob() is a system call to the glob() function (sick, isn't it?). It's a convenient function, that allows wild-carded listing of files. It accepts a GLOB_NOSORT flag that prevents the sort. By default, the listed files are sorted. The impact of the execution time is lower than the one from array_merge(). Listing 28k files : glob() with default values : 16s glob() with NOSORT         : 12s In fact, the alternative to glob() is scandir(). scandir() also listing files, though it doesn't handle wild-cards. Scandir(), on the other hand, also has some default sorting. It is always possible to SCANDIR_SORT_NONE, which is not sort. ## How to speed up your code ? As often stated, use functions that do only what they are supposed to do and not more. Unless it is a needed feature for your code, you may gain performances by simply using the following : - replace array_unique() with [array_keys](http://www.php.net/array_keys)([array_count_values](http://www.php.net/array_count_values)()), foreach() loop or [array_flip](http://www.php.net/array_flip)(array_flip()) are among the solutions. - glob() should be replaced by scandir(), or set with the option GLOB_NOSORT. Using [GlobIterator](http://www.php.net/globiterator)() from the SPL lib is a good idea as it doesn't sort. - scandir() should use the SCANDIR_NO_SORT option. It may also be replaced with [DirectoryIterator](http://www.php.net/DirectoryIterator) or [RecursiveDirectoryIterator](http://www.php.net/RecursiveDirectoryIterator) ## Automated check your code All those three are reported by [exakat](https://www.exakat.io/) in its '[Performances](https://exakat.readthedocs.io/en/latest/Reference/Rulesets/Performances.html#ruleset-performances)' section, among others. Simply run the default analysis and spot performances potentials. Array_unique() is quite common, with roughly 1 project out of 3 (36 % ) of code source using it at some point. Glob() and scandir() are scarcer, especially used on large folders : 10 %. And remember, read the docs, once in a while, just to keep you updated. Or use a static analyzer, that reads the docs often. --- # The 100 PHP functions in 2022 Source: https://www.exakat.io/the-100-php-functions-in-2022/ # The 100 PHP functions that you have to know ### There is a newer version of this top, in 2024. Here is the top 100 PHP functions : it is the list of the most often used PHP native functions. The functions are named, and ranked from 1 to 100. The other 4500 functions are not ranked here. The frequency column represents how often this function is used across PHP code repositories : the reference corpus is a list of 2500 PHP Open Source projects (top 1000 composer, github/gitlab/gitee public repo, downloaded archives...). They were audited with [Exakat static analysis engine](https://www.exakat.io/), version 2.4.7. The average is the number of time the function is called within one project. For example, 4 project out of 5 uses the [count()](https://php.net/count) function, and when used, [count()](https://php.net/count) is called around 61 times. It is both a popular and heavily used function. On the other hand, [dirname()](http://php.net/dirname) is popular but rarely used (56% for 13 usages) Click on the link to go to the documentation. Some insight at the bottom of the top 100.   ## The 100 most popular PHP functions | **Rank** | **Function** | **Frequency** | **Average** | | -------- | ------------ | ------------- | ----------- | | 1 | [file_exists](https://www.php.net/manual/en/function.file-exists.php) | 62.80% | 13 | | 2 | [str_replace](https://www.php.net/manual/en/function.str-replace.php) | 58.20% | 33 | | 3 | [implode](https://www.php.net/manual/en/function.implode.php) | 57.31% | 35 | | 4 | [count](https://www.php.net/manual/en/function.count.php) | 56.42% | 61 | | 5 | [dirname](https://www.php.net/manual/en/function.dirname.php) | 56.38% | 13 | | 6 | [substr](https://www.php.net/manual/en/function.substr.php) | 55.84% | 53 | | 7 | [sprintf](https://www.php.net/manual/en/function.sprintf.php) | 55.26% | 80 | | 8 | [strpos](https://www.php.net/manual/en/function.strpos.php) | 55.07% | 24 | | 9 | [array_merge](https://www.php.net/manual/en/function.array-merge.php) | 54.80% | 35 | | 10 | [in_array](https://www.php.net/manual/en/function.in-array.php) | 51.39% | 33 | | 11 | [explode](https://www.php.net/manual/en/function.explode.php) | 51.35% | 26 | | 12 | [strlen](https://www.php.net/manual/en/function.strlen.php) | 50.97% | 35 | | 13 | [is_array](https://www.php.net/manual/en/function.is-array.php) | 50.73% | 46 | | 14 | [array_key_exists](https://www.php.net/manual/en/function.array-key-exists.php) | 49.54% | 28 | | 15 | [array_keys](https://www.php.net/manual/en/function.array-keys.php) | 49.27% | 16 | | 16 | [preg_match](https://www.php.net/manual/en/function.preg-match.php) | 46.52% | 28 | | 17 | [file_get_contents](https://www.php.net/manual/en/function.file-get-contents.php) | 46.48% | 8 | | 18 | [is_file](https://www.php.net/manual/en/function.is-file.php) | 44.43% | 6 | | 19 | [array_values](https://www.php.net/manual/en/function.array-values.php) | 42.81% | 7 | | 20 | [file_put_contents](https://www.php.net/manual/en/function.file-put-contents.php) | 42.46% | 4 | | 21 | [trim](https://www.php.net/manual/en/function.trim.php) | 41.57% | 25 | | 22 | [method_exists](https://www.php.net/manual/en/function.method-exists.php) | 41.49% | 8 | | 23 | [defined](https://www.php.net/manual/en/function.defined.php) | 40.87% | 19 | | 24 | [is_string](https://www.php.net/manual/en/function.is-string.php) | 40.76% | 20 | | 25 | [function_exists](https://www.php.net/manual/en/function.function-exists.php) | 40.29% | 20 | | 26 | [array_map](https://www.php.net/manual/en/function.array-map.php) | 39.33% | 12 | | 27 | [preg_replace](https://www.php.net/manual/en/function.preg-replace.php) | 39.02% | 17 | | 28 | [strtolower](https://www.php.net/manual/en/function.strtolower.php) | 38.94% | 19 | | 29 | [is_dir](https://www.php.net/manual/en/function.is-dir.php) | 38.67% | 6 | | 30 | [strtr](https://www.php.net/manual/en/function.strtr.php) | 38.01% | 5 | | 31 | [call_user_func](https://www.php.net/manual/en/function.call-user-func.php) | 37.01% | 4 | | 32 | [strrpos](https://www.php.net/manual/en/function.strrpos.php) | 36.70% | 4 | | 33 | [call_user_func_array](https://www.php.net/manual/en/function.call-user-func-array.php) | 36.43% | 3 | | 34 | [array_flip](https://www.php.net/manual/en/function.array-flip.php) | 35.89% | 2 | | 35 | [fwrite](https://www.php.net/manual/en/function.fwrite.php) | 35.89% | 4 | | 36 | [rtrim](https://www.php.net/manual/en/function.rtrim.php) | 35.69% | 6 | | 37 | [filter_var](https://www.php.net/manual/en/function.filter-var.php) | 35.65% | 2 | | 38 | [ini_get](https://www.php.net/manual/en/function.ini-get.php) | 35.15% | 5 | | 39 | [json_decode](https://www.php.net/manual/en/function.json-decode.php) | 34.76% | 7 | | 40 | [array_filter](https://www.php.net/manual/en/function.array-filter.php) | 34.76% | 7 | | 41 | [realpath](https://www.php.net/manual/en/function.realpath.php) | 34.15% | 4 | | 42 | [header](https://www.php.net/manual/en/function.header.php) | 34.07% | 7 | | 43 | [class_exists](https://www.php.net/manual/en/function.class-exists.php) | 34.03% | 12 | | 44 | [json_encode](https://www.php.net/manual/en/function.json-encode.php) | 33.49% | 9 | | 45 | [trigger_error](https://www.php.net/manual/en/function.trigger-error.php) | 32.95% | 5 | | 46 | [get_class](https://www.php.net/manual/en/function.get-class.php) | 32.95% | 13 | | 47 | [spl_autoload_register](https://www.php.net/manual/en/function.spl-autoload-register.php) | 32.10% | 1 | | 48 | [unlink](https://www.php.net/manual/en/function.unlink.php) | 30.43% | 5 | | 49 | [headers_sent](https://www.php.net/manual/en/function.headers-sent.php) | 29.78% | 1 | | 50 | [is_object](https://www.php.net/manual/en/function.is-object.php) | 29.31% | 11 | | 51 | [mkdir](https://www.php.net/manual/en/function.mkdir.php) | 29.20% | 2 | | 52 | [array_shift](https://www.php.net/manual/en/function.array-shift.php) | 28.69% | 6 | | 53 | [spl_autoload_unregister](https://www.php.net/manual/en/function.spl-autoload-unregister.php) | 28.54% | 1 | | 54 | [apcu_fetch](https://www.php.net/manual/en/function.apcu-fetch.php) | 28.23% | 0 | | 55 | [array_pop](https://www.php.net/manual/en/function.array-pop.php) | 28.11% | 5 | | 56 | [stream_resolve_include_path](https://www.php.net/manual/en/function.stream-resolve-include-path.php) | 27.84% | 0 | | 57 | [is_int](https://www.php.net/manual/en/function.is-int.php) | 27.65% | 5 | | 58 | [apcu_add](https://www.php.net/manual/en/function.apcu-add.php) | 27.57% | 0 | | 59 | [getcwd](https://www.php.net/manual/en/function.getcwd.php) | 27.49% | 1 | | 60 | [strtoupper](https://www.php.net/manual/en/function.strtoupper.php) | 27.42% | 6 | | 61 | [is_numeric](https://www.php.net/manual/en/function.is-numeric.php) | 26.95% | 9 | | 62 | [array_unique](https://www.php.net/manual/en/function.array-unique.php) | 26.91% | 4 | | 63 | [ltrim](https://www.php.net/manual/en/function.ltrim.php) | 26.60% | 4 | | 64 | [basename](https://www.php.net/manual/en/function.basename.php) | 26.57% | 4 | | 65 | [str_repeat](https://www.php.net/manual/en/function.str-repeat.php) | 26.26% | 7 | | 66 | [fopen](https://www.php.net/manual/en/function.fopen.php) | 25.91% | 6 | | 67 | [array_slice](https://www.php.net/manual/en/function.array-slice.php) | 24.94% | 3 | | 68 | [getenv](https://www.php.net/manual/en/function.getenv.php) | 24.94% | 4 | | 69 | [var_export](https://www.php.net/manual/en/function.var-export.php) | 24.71% | 3 | | 70 | [preg_match_all](https://www.php.net/manual/en/function.preg-match-all.php) | 24.67% | 3 | | 71 | [is_callable](https://www.php.net/manual/en/function.is-callable.php) | 24.48% | 4 | | 72 | [reset](https://www.php.net/manual/en/function.reset.php) | 24.44% | 4 | | 73 | [preg_split](https://www.php.net/manual/en/function.preg-split.php) | 24.40% | 3 | | 74 | [fclose](https://www.php.net/manual/en/function.fclose.php) | 24.36% | 5 | | 75 | [array_unshift](https://www.php.net/manual/en/function.array-unshift.php) | 24.25% | 3 | | 76 | [gettype](https://www.php.net/manual/en/function.gettype.php) | 24.21% | 4 | | 77 | [end](https://www.php.net/manual/en/function.end.php) | 23.90% | 3 | | 78 | [array_search](https://www.php.net/manual/en/function.array-search.php) | 23.82% | 3 | | 79 | [max](https://www.php.net/manual/en/function.max.php) | 23.78% | 5 | | 80 | [extension-loaded](https://www.php.net/manual/en/function.extension-loaded.php) | 23.59% | 3 | | 81 | [is_bool](https://www.php.net/manual/en/function.is-bool.php) | 23.55% | 2 | | 82 | [version_compare](https://www.php.net/manual/en/function.version-compare.php) | 22.82% | 3 | | 83 | [preg_quote](https://www.php.net/manual/en/function.preg-quote.php) | 22.78% | 3 | | 84 | [time](https://www.php.net/manual/en/function.time.php) | 22.70% | 9 | | 85 | [ucfirst](https://www.php.net/manual/en/function.ucfirst.php) | 22.66% | 4 | | 86 | [ksort](https://www.php.net/manual/en/function.ksort.php) | 22.66% | 2 | | 87 | [preg_replace_callback](https://www.php.net/manual/en/function.preg-replace-callback.php) | 22.54% | 3 | | 88 | [md5](https://www.php.net/manual/en/function.md5.php) | 22.51% | 4 | | 89 | [array_reverse](https://www.php.net/manual/en/function.array-reverse.php) | 22.47% | 2 | | 90 | [array_diff](https://www.php.net/manual/en/function.array-diff.php) | 22.12% | 2 | | 91 | [is_readable](https://www.php.net/manual/en/function.is-readable.php) | 22.00% | 1 | | 92 | [microtime](https://www.php.net/manual/en/function.microtime.php) | 21.96% | 4 | | 93 | [copy](https://www.php.net/manual/en/function.copy.php) | 21.81% | 1 | | 94 | [parse_url](https://www.php.net/manual/en/function.parse-url.php) | 21.81% | 2 | | 95 | [is_null](https://www.php.net/manual/en/function.is-null.php) | 21.50% | 17 | | 96 | [base64_encode](https://www.php.net/manual/en/function.base64-encode.php) | 21.42% | 3 | | 97 | [current](https://www.php.net/manual/en/function.current.php) | 21.38% | 2 | | 98 | [is_resource](https://www.php.net/manual/en/function.is-resource.php) | 21.08% | 3 | | 99 | [serialize](https://www.php.net/manual/en/function.serialize.php) | 20.77% | 4 | | 100 | [key](https://www.php.net/manual/en/function.key.php) | 20.57% | 2 | ## Top 100 insights - The most commonly used PHP functions are [string](https://www.php.net/manual/en/language.types.string.php) functions, and then [arrays](https://www.php.net/manual/en/language.types.array.php), then [files](https://www.php.net/manual/en/ref.filesystem.php). Math is probably out of the scope, as it is mostly based on operators - Databases are probably out of scope, as they are based on classes - No recently deprecated function is in the top 100. - Usage of [composer](https://getcomposer.org/) may also reduce the need for some native function, by federating them in a component or two, and reducing their overall usage. For example, [monolog](https://github.com/Seldaek/monolog) may both use [log](https://php.net/log)() and reduce its usage across the Open Source community. - Extension that makes it to the top 100 : [filter](https://www.php.net/manual/en/book.filter.php), [json](https://www.php.net/manual/json/book.filter.php), [apcu](https://www.php.net/manual/apcu/book.filter.php). - `md5` is the only widely used crypto function. `Sha1` is the second (#147). - Debug functions, such as print_r() or var_dump() are out of the top 100. - `array`, `echo`, `print`, `empty`, `isset` and other language constructs were not counted as functions in this ranking. They are probably trusting the first ranks anyway. - Several functions should be replaced by operators : `array_push`, `is_object`, `func_get_arg`, `chr`, `call_user_func`. - Types are still a major concern of PHP code : is it a string? is it an object? No, it's a resource! Type system may go a long way, but there are still return values that needs testing. - There is also a [PHP top 100 for the PHP classes](https://www.exakat.io/en/top-100-php-classes-that-you-should-know/). Wild ideas include top 100 PHP constants, operators, variable names,... Why not? ## Challenges If you are learning PHP, it is a good idea to review the 100 functions ranked here : they are the features you'll find the most often when landing on a coding team. They are not the only ones, but you'll be less surprised when meeting them. ## More details? Follow us on [@exakat](https://twitter.com/exakat) if you want more details, more stats --- # The Top 100 PHP functions in 2024 Source: https://www.exakat.io/the-top-100-php-functions-in-2024/ # The 100 PHP functions that you have to know Here is the top 100 PHP functions : it is the list of the most often used PHP native functions. The functions are named, and ranked from 1 to 100. The other 4500 functions are not ranked here. The frequency column represents how often this function is used across PHP code repositories : the reference corpus is a list of 2500 PHP Open Source projects (top 1000 composer, github/gitlab/gitee public repo, downloaded archives...). They were audited with [Exakat static analysis engine](https://www.exakat.io/), version 2.4.7. The average is the number of time the function is called within one project. For example, 4 project out of 5 uses the [count()](https://php.net/count) function, and when used, [count()](https://php.net/count) is called around 61 times. It is both a popular and heavily used function. On the other hand, [dirname()](http://php.net/dirname) is popular but rarely used (56% for 13 usages) Click on the link to go to the documentation. Some insight at the bottom of the top 100.   ## The 100 most popular PHP functions | 1 | [file_exists](https://www.php.net/manual/en/function.file-exists.php) | 66.05% | 18 | | --- | --------------------------------------------------------------------- | ------ | --- | | 2 | [count](https://www.php.net/manual/en/function.count.php) | 63.67% | 87 | | 3 | [str_replace](https://www.php.net/manual/en/function.str-replace.php) | 63.58% | 51 | | 4 | [implode](https://www.php.net/manual/en/function.implode.php) | 62.72% | 48 | | 5 | [substr](https://www.php.net/manual/en/function.substr.php) | 60.30% | 70 | | 6 | [array_merge](https://www.php.net/manual/en/function.array-merge.php) | 60.02% | 51 | | 7 | [sprintf](https://www.php.net/manual/en/function.sprintf.php) | 59.89% | 103 | | 8 | [dirname](https://www.php.net/manual/en/function.dirname.php) | 58.91% | 17 | | 9 | [explode](https://www.php.net/manual/en/function.explode.php) | 58.30% | 37 | | 10 | [in_array](https://www.php.net/manual/en/function.in-array.php) | 58.27% | 49 | | 11 | [strpos](https://www.php.net/manual/en/function.strpos.php) | 58.08% | 31 | | 12 | [is_array](https://www.php.net/manual/en/function.is-array.php) | 58.05% | 73 | | 13 | [strlen](https://www.php.net/manual/en/function.strlen.php) | 56.37% | 45 | | 14 | [array_key_exists](https://www.php.net/manual/en/function.array-key-exists.php) | 54.87% | 88 | | 15 | [array_keys](https://www.php.net/manual/en/function.array-keys.php) | 54.72% | 23 | | 16 | [preg_match](https://www.php.net/manual/en/function.preg-match.php) | 52.02% | 40 | | 17 | [file_get_contents](https://www.php.net/manual/en/function.file-get-contents.php) | 51.76% | 11 | | 18 | [trim](https://www.php.net/manual/en/function.trim.php) | 48.49% | 37 | | 19 | [is_string](https://www.php.net/manual/en/function.is-string.php) | 48.46% | 27 | | 20 | [array_values](https://www.php.net/manual/en/function.array-values.php) | 48.46% | 10 | | 21 | [is_file](https://www.php.net/manual/en/function.is-file.php) | 46.90% | 7 | | 22 | [method_exists](https://www.php.net/manual/en/function.method-exists.php) | 46.62% | 11 | | 23 | [array_map](https://www.php.net/manual/en/function.array-map.php) | 46.11% | 17 | | 24 | [file_put_contents](https://www.php.net/manual/en/function.file-put-contents.php) | 46.11% | 7 | | 25 | [strtolower](https://www.php.net/manual/en/function.strtolower.php) | 45.44% | 26 | | 26 | [function_exists](https://www.php.net/manual/en/function.function-exists.php) | 44.97% | 21 | | 27 | [preg_replace](https://www.php.net/manual/en/function.preg-replace.php) | 44.40% | 23 | | 28 | [defined](https://www.php.net/manual/en/function.defined.php) | 43.44% | 27 | | 29 | [is_dir](https://www.php.net/manual/en/function.is-dir.php) | 41.70% | 8 | | 30 | [json_decode](https://www.php.net/manual/en/function.json-decode.php) | 41.54% | 10 | | 31 | [json_encode](https://www.php.net/manual/en/function.json-encode.php) | 41.16% | 15 | | 32 | [call_user_func](https://www.php.net/manual/en/function.call-user-func.php) | 41.06% | 5 | | 33 | [array_filter](https://www.php.net/manual/en/function.array-filter.php) | 41.00% | 10 | | 34 | [strtr](https://www.php.net/manual/en/function.strtr.php) | 40.55% | 6 | | 35 | [fwrite](https://www.php.net/manual/en/function.fwrite.php) | 40.08% | 7 | | 36 | [rtrim](https://www.php.net/manual/en/function.rtrim.php) | 39.85% | 8 | | 37 | [strrpos](https://www.php.net/manual/en/function.strrpos.php) | 39.54% | 4 | | 38 | [call_user_func_array](https://www.php.net/manual/en/function.call-user-func-array.php) | 39.41% | 4 | | 39 | [filter_var](https://www.php.net/manual/en/function.filter-var.php) | 39.25% | 3 | | 40 | [class_exists](https://www.php.net/manual/en/function.class-exists.php) | 39.22% | 17 | | 41 | [header](https://www.php.net/manual/en/function.header.php) | 38.71% | 11 | | 42 | [array_flip](https://www.php.net/manual/en/function.array-flip.php) | 38.62% | 3 | | 43 | [realpath](https://www.php.net/manual/en/function.realpath.php) | 38.11% | 6 | | 44 | [ini_get](https://www.php.net/manual/en/function.ini-get.php) | 38.01% | 6 | | 45 | [get_class](https://www.php.net/manual/en/function.get-class.php) | 36.01% | 14 | | 46 | [is_object](https://www.php.net/manual/en/function.is-object.php) | 35.03% | 15 | | 47 | [unlink](https://www.php.net/manual/en/function.unlink.php) | 34.96% | 8 | | 48 | [trigger_error](https://www.php.net/manual/en/function.trigger-error.php) | 34.84% | 8 | | 49 | [array_shift](https://www.php.net/manual/en/function.array-shift.php) | 34.71% | 8 | | 50 | [is_int](https://www.php.net/manual/en/function.is-int.php) | 33.76% | 7 | | 51 | [spl_autoload_register](https://www.php.net/manual/en/function.spl-autoload-register.php) | 33.57% | 1 | | 52 | [is_numeric](https://www.php.net/manual/en/function.is-numeric.php) | 32.93% | 13 | | 53 | [strtoupper](https://www.php.net/manual/en/function.strtoupper.php) | 32.93% | 9 | | 54 | [mkdir](https://www.php.net/manual/en/function.mkdir.php) | 32.93% | 4 | | 55 | [array_pop](https://www.php.net/manual/en/function.array-pop.php) | 32.52% | 7 | | 56 | [ltrim](https://www.php.net/manual/en/function.ltrim.php) | 31.95% | 5 | | 57 | [headers_sent](https://www.php.net/manual/en/function.headers-sent.php) | 31.95% | 2 | | 58 | [fopen](https://www.php.net/manual/en/function.fopen.php) | 31.88% | 8 | | 59 | [array_unique](https://www.php.net/manual/en/function.array-unique.php) | 31.79% | 6 | | 60 | [str_repeat](https://www.php.net/manual/en/function.str-repeat.php) | 31.47% | 9 | | 61 | [basename](https://www.php.net/manual/en/function.basename.php) | 31.25% | 6 | | 62 | [array_slice](https://www.php.net/manual/en/function.array-slice.php) | 30.39% | 5 | | 63 | [fclose](https://www.php.net/manual/en/function.fclose.php) | 29.91% | 7 | | 64 | [time](https://www.php.net/manual/en/function.time.php) | 29.47% | 16 | | 65 | [is_callable](https://www.php.net/manual/en/function.is-callable.php) | 29.37% | 5 | | 66 | [preg_match_all](https://www.php.net/manual/en/function.preg-match-all.php) | 29.28% | 4 | | 67 | [is_bool](https://www.php.net/manual/en/function.is-bool.php) | 29.15% | 3 | | 68 | [microtime](https://www.php.net/manual/en/function.microtime.php) | 28.74% | 5 | | 69 | [spl_autoload_unregister](https://www.php.net/manual/en/function.spl-autoload-unregister.php) | 28.71% | 1 | | 70 | [var_export](https://www.php.net/manual/en/function.var-export.php) | 28.61% | 4 | | 71 | [array_unshift](https://www.php.net/manual/en/function.array-unshift.php) | 28.58% | 3 | | 72 | [max](https://www.php.net/manual/en/function.max.php) | 28.42% | 7 | | 73 | [array_search](https://www.php.net/manual/en/function.array-search.php) | 28.29% | 4 | | 74 | [apcu_fetch](https://www.php.net/manual/en/function.apcu-fetch.php) | 28.29% | 0 | | 75 | [extension_loaded](https://www.php.net/manual/en/function.extension-loaded.php) | 28.20% | 4 | | 76 | [getcwd](https://www.php.net/manual/en/function.getcwd.php) | 28.17% | 2 | | 77 | [preg_split](https://www.php.net/manual/en/function.preg-split.php) | 28.14% | 4 | | 78 | [reset](https://www.php.net/manual/en/function.reset.php) | 28.10% | 6 | | 79 | [end](https://www.php.net/manual/en/function.end.php) | 28.04% | 4 | | 80 | [gettype](https://www.php.net/manual/en/function.gettype.php) | 28.04% | 4 | | 81 | [stream_resolve_include_path](https://www.php.net/manual/en/function.stream-resolve-include-path.php) | 27.85% | 0 | | 82 | [md5](https://www.php.net/manual/en/function.md5.php) | 27.72% | 6 | | 83 | [getenv](https://www.php.net/manual/en/function.getenv.php) | 27.66% | 6 | | 84 | [apcu_add](https://www.php.net/manual/en/function.apcu-add.php) | 27.41% | 0 | | 85 | [ucfirst](https://www.php.net/manual/en/function.ucfirst.php) | 27.34% | 7 | | 86 | [ksort](https://www.php.net/manual/en/function.ksort.php) | 27.31% | 3 | | 87 | [date](https://www.php.net/manual/en/function.date.php) | 27.18% | 14 | | 88 | [is_null](https://www.php.net/manual/en/function.is-null.php) | 26.90% | 27 | | 89 | [parse_url](https://www.php.net/manual/en/function.parse-url.php) | 26.61% | 3 | | 90 | [preg_quote](https://www.php.net/manual/en/function.preg-quote.php) | 26.61% | 4 | | 91 | [array_reverse](https://www.php.net/manual/en/function.array-reverse.php) | 26.58% | 3 | | 92 | [array_diff](https://www.php.net/manual/en/function.array-diff.php) | 26.45% | 4 | | 93 | [base64_encode](https://www.php.net/manual/en/function.base64-encode.php) | 26.14% | 4 | | 94 | [version_compare](https://www.php.net/manual/en/function.version-compare.php) | 26.04% | 4 | | 95 | [preg_replace_callback](https://www.php.net/manual/en/function.preg-replace-callback.php) | 25.91% | 4 | | 96 | [current](https://www.php.net/manual/en/function.current.php) | 25.25% | 3 | | 97 | [round](https://www.php.net/manual/en/function.round.php) | 25.25% | 7 | | 98 | [min](https://www.php.net/manual/en/function.min.php) | 25.15% | 4 | | 99 | [serialize](https://www.php.net/manual/en/function.serialize.php) | 25.02% | 6 | ## Top 100 insights - The most commonly used PHP functions are [string](https://www.php.net/manual/en/language.types.string.php) functions, and then [arrays](https://www.php.net/manual/en/language.types.array.php), then [files](https://www.php.net/manual/en/ref.filesystem.php). Math functions are probably out of the ranking, as they are mostly based on operators - Databases functions (pdo, mysqli*, pg_*...) are probably out of scope, as they are based on classes or components - No recently deprecated function is in the top 100. - Usage of [composer](https://getcomposer.org/) may also reduce the need for some native function, by federating them in a component, and reducing their overall usage. For example, [monolog](https://github.com/Seldaek/monolog) may use [log](https://php.net/log)() and reduce its usage across the Open Source community. - Extension that makes it to the top 100 : [filter](https://www.php.net/manual/en/book.filter.php), [json](https://www.php.net/manual/json/book.filter.php), [apcu](https://www.php.net/manual/apcu/book.filter.php). - json_encode() and json_decode() are now used with the same exact frequency, or almost. - `md5` is the only widely used crypto function. hash() is the next, in 159th position (not shown). This must change! - Debug functions, such as print_r() or var_dump() are out of the top 100. var_export() is a more ambiguous case. - `array`, `echo`, `print`, `empty`, `isset` and other language constructs were not counted as functions in this ranking. They are probably trusting the first ranks anyway. - Several functions could and should be replaced by operators : `is_object()`, call_user_func(), call_user_func_array() - max() is more often than min() - Types are still a major concern of PHP code : is it a string? is it an object? No, it's a resource! Type system may go a long way, but there are still return values that needs testing. - There is also a [PHP top 100 for the PHP classes](https://www.exakat.io/en/top-100-php-classes-that-you-should-know/). Well, ## Challenges If you are learning PHP, it is a good idea to review the 100 functions ranked here : they are the features you'll find the most often when landing on a coding team. They are not the only ones, but you'll be less surprised when meeting them. ## More details? Follow us on [BlueSky](https://bsky.app/profile/dseguy.bsky.social) or [mastodon](https://phpc.social/@dseguy) if you want more details, more stats --- # How to call a method in PHP Source: https://www.exakat.io/call-a-method-in-php/ ## All the ways to call a method in PHP There are many ways to call a method in PHP. Method calling is a very common task, and, although the most common syntax is the overwhelming majority, there are some niche syntaxes for specific usages. And some good old syntaxes too. Since they are scattered across the manual, we have gathered them here. All of them. ### The function classic way Hardcoded, directly in the source. This is the syntax that everyone learn first. Note that the function name may be aliased, or imported from another namespace with the `use`expression. The alias only affects the call of the function, and not the function itself. ### With a dynamic name A dynamic name means that the function's name is stored in a string. The string is usually stored in a variable or similar, and it is used in place of the function name: PHP reads the string in the variable, and the correct function is called. Always use the fully qualified name in the string, as local names are not solved. This usually leads to surprises when inside a namespace. As this must be done even for locally defined functions, `__NAMESPACE__` comes to the rescue. ### With a string The previous entry has one edge case: it is possible to use a string as the name of the function and call it directly: indeed, there is no need to use a variable. Compared to the classic way, the string looses the aliasing features, provided by the `use`expression. On the other hand, it is also possible to use the double quotes strings, and put some variables in it. This is a dynamic syntax. ### With a closure Instead of calling the function by referencing its absolute name, PHP creates closures from any function or method. Since PHP 8.2, the `...` operator, used as only argument in the function, returns a closure that represents the function. Then, that closure may be used to actually call the original function. Also, for fun, there can be many closures creation before, finally, calling it. This is useless syntax, but rather funny. ### With a call to call_user_func() Why not call a function by calling ... another function? Use the native `call_user_func` or `call_user_func_array` to call any function or method. The first parameter is the name of the function, and the same rules we have already seen apply : make the name of the function fully qualified. `call_user_func` and `call_user_func_array` are useful when both the function and the parameters are dynamic. ### Calling methods with the array syntax The previous entry is also useful to call methods. Except that methods don't have the same string syntax, so we need to introduce the array syntax. Compared to calling a function, a method requires two parts: the object or the class, and the method name. And, of course, the parameters. PHP features a special syntax, based on arrays: to call a method on an object, create an array of two elements, with the object and the method name. Then, call it like that. Like for the strings, it is possible to call directly on the literal array, as long as it contains the correct information. ### Calling methods with the array syntax and the relative class names The relative class names are `self`, `static` and `parent`. They are not class names per se, as it is not allowed to create classes with such names. But they to represent actual classes, which vary from definition to definition. The relative class names are collected at definition time. When the array is moved around the program, it will always refer to the method that was targeted initially, not at the point of execution. Also, note that PHP doesn't allow anymore the usage of any of the relative classnames in strings, since PHP 8.2. The code below produces a string error message: `Class "static" not found` ### Calling static methods The previous syntax is also available for static methods: then, the first element of the array may be an object or the name of the class. PHP picks up anything that is useful to make the call succeed. ### Calling methods with a string Calling methods with a string is possible, as long as the method is static. Then, the string should be written as `class::method`, and target a static method. That approach is not possible with objects, which are not compatible with a string. ### Calling the anonymous method One more way to call a method is to call it directly from an object. That is the purpose of the `__invoke` method: it is a magic method, which is called automatically when the object itself is used as a function name. Like this: Note that `__invoke` is not anonymous, as ... it has a name. On the other hand, calling `$object()` means that there is no need for the name of the method to be called, so this is anonymous. ### The Reflection call The [reflection API](https://www.php.net/manual/en/book.reflection.php) allows for PHP code introspection, reading descriptive features of any defined structures. It may be lesser known that such structures are still executable, and this applies to the functions and methods. In the case of methods and functions, the [reflectionFunction](https://www.php.net/manual/en/class.reflectionfunction.php) class has an [invoke()](https://www.php.net/manual/en/reflectionfunction.invoke.php) method and a [invokeArgs()](https://www.php.net/manual/en/reflectionfunction.invoke.php) method. Thanks to [Benoit Viguier](https://bsky.app/profile/b-viguier.bsky.social) for the reminder. ### The evergreen eval() call One always have remember old PHP features, and eval() is definitely one of them. Since eval() requires some PHP code in a string, it is another way to call a method or a function or anything really. Thanks to [Anna Filina](https://bsky.app/profile/afilina.bsky.social) for the reminder. ### Calling methods the classic way Finally, the classic way to call methods: this is achieved with the object operator `->` and the static operator `::`. ## That's all folks! We have covered no less than eleven (11!!) different ways to call PHP methods and functions. The first and last are the most commonly used, with good reasons. The other ones covers niche usages, such as dynamic method call, or dynamic parameter collections. --- # PHP Tips Archive: the Quirks and Oddities of the Language Source: https://www.exakat.io/php-tips/ # The Quirks and Oddities of the Language: PHP Tips Archive PHP, the server-side platform that powers the web, is known for its versatility and ease of use. However, beneath its seemingly straightforward syntax lies a world of peculiarities, oddities, and unexpected behaviors. If you're looking to explore the** [quirks of PHP](https://php-tips.readthedocs.io/en/latest/)** in a fun and enlightening way, the "PHP Tips archive" is your go-to resource. ## Unveiling the PHP Tips Archive The PHP Tips archive is a treasure trove of weird syntax, edge cases, and unexpected behaviors of the PHP language. Compiled by a community of PHP enthusiasts, this archive sheds light on the idiosyncrasies that make PHP a unique language in the web development landscape. ## Weird Syntax and Edge Cases One of the highlights of the PHP Tips archive is its exploration of weird syntax and edge cases that may leave even seasoned developers scratching their heads. From unusual ways to define variables to impossible function invocations, the archive delves into the depths of PHP's syntax, showcasing the language's flexibility and, at times, its unpredictability. ## PHP Unexpected Behaviors PHP Tips archive goes beyond the basics, unraveling the unexpected behaviors that can catch developers off guard. Whether it's the infamous type juggling or the nested nuances of keywords usage, the archive demonstrates how PHP's design choices can lead to surprising outcomes. Understanding these quirks is not only amusing but also crucial for writing bug-free code. ## Not Recommended for Everyday Use While the PHP Tips archive is a goldmine of knowledge, it comes with a disclaimer – most of showcased tips are not recommended for everyday use. They rarely come with an immediate application. In a production environment, relying on such unconventional practices can lead to maintenance challenges and unpredictable results. On the other hand, exploring these tips in a controlled environment can be a valuable learning experience. ## Learning Tool for PHP Enthusiasts The PHP Tips archive is more than just a collection of oddities; it serves as an invaluable learning tool for PHP developers. By unraveling the intricacies of the language, developers gain a deeper understanding of PHP's inner workings. This knowledge can empower them to write more efficient, readable code, or simply take advantage of a rare feature. ## Contributions Are Welcome The **[archive resides](https://github.com/dseguy/phptips)** on a GitHub repository: it is open to contributions from PHP tip researchers worldwide. If you stumble upon a new PHP quirk or want to share your insights, you can actively contribute in expanding this resource for the benefit of the PHP community. ## PHP tips archive for everyone In the world of web development, understanding the nuances of the tools we use is key to becoming a proficient developer. The PHP Tips archive provides a humorous and insightful journey into the depths of PHP's quirks, offering developers an opportunity to enhance their skills and share their knowledge. So, if you're ready to embark on a unique adventure through the eccentricities of PHP, dive into the PHP Tips archive and prepare to be both entertained and enlightened. --- # Asymmetric visibility for PHP properties Source: https://www.exakat.io/asymmetric-visibility-for-php-properties/ # Asymmetric visibility for PHP properties One of the lesser visible feature coming to PHP 8.4, pun intended, is the assymetric visibility for PHP properties. It is an upgrade of the current visibility, and also, a number of refined features over readonly, properties hooks or magic methods. It is not for everyone, but it might be quite useful. Let's take a look! ## Asymmetric Visibility Personnally, I didn't know that visibility was symmetric. How can `public`, `protected` and `private` have some symmetry? It actually boils down to read and write. A property is both `private` for reading and writing. That's all. Here, the public property acts as a rustic getter/setter (or lack of). It makes sens for `$side`, but it is more awkward for the `$area` property, which should actually be readable from anywhere, but writeable only with `$side`. For PHP 8.4, Ilija Tovilo and Larry Garfield, are now introducing a new visibility setting for writing. Literally, a `set` visibility: `private(set)`. With this new setting, the `$area` property may be read with `public` visibility: a.k.a., everywhere. And it can only set with `private` visibility: a.k.a. from inside the class. Here, it is the constructor. Et voila! The read and write visibilities are now asymmetric, as they do not have the same scope of application. ## Which visibilities for which properties? With now 2 sets of visibilities, PHP is introducing no less than 9 different configurations of visibilities. This is quite a lot, in fact, and it seems it is going to make the visibility declaration harder to read. But, don't get scared, there are some classic user-friendliness down the road. So, let's see if we can get a better understanding of all these visibilities. ### The three symmetric visibilities First, let's see the symmetric visibilities. After all, `private private(set)`, `protected protected(set)` and `public public(set)` are the symmetric visibilities. We have had them ever since the conception of PHP (or almost... sorry Rasmus), and we already have a nice short hand to write them : `private`, `protected`, `public`. So, no change for these: keep them short and backward compatible! Also, the new token `private(set)` (and its cousins), are an addition to the language. This means they can be used alone, without the classic read visibilities. And, what happens when the visibility is omitted? It is by default `public`. This behavior also stays in PHP 8.4, even when setting explicitely the asymmetric visibility. ### The three asymmetric visibilities The three asymmetric visibilities are the following : `public private(set)`, `protected private(set)` and `public protected(set)`. These are not symmetric (see above), and they provide a distinct scope for reading (`public`), and one for writing (`private(set)`. This is the example we had with the initial `Square` class. ### The three illegal visibilities The last three asymmetric visibilities are the following : `private public(set)`, `private protected(set)` and `protected public(set)`. These are the last, where the host class cannot write its properties, but can get them written from everywhere in the application. All these make little sense, and are forbidden by PHP. For those of you who have used writeable-only files on the system, the comparison is not straightforward. While writing in a file where one can't read is a valid usage on the OS (think, logs, which are read elsewhere), this is not applicable for an object. It would be easy to circumvent this limitation and access the property anyway. ### Syntax summary | | | private | protected | public | | | --- | --- | ------- | --------- | ------ | --- | | | **private(set)** | Sym. | Illegal | Illegal | | | | **protected(set)** | Useful | Sym. | Illegal | | | | **public(set)** | Useful | Useful | Sym. | | # A few precautions ## Asymmetry also for static properties Static properties also support asymmetric visibility. Note that the RFC mentions that static properties would not be covered, but a few manual tests showed that it is not the case. ## Only for typed properties Asymmetric visibility is only for typed properties. It's another compile-time syntax error. ## The PHP 8.3 error message Obviously, this new syntax is not backward compatible. This means that, once you have adopted the asymmetric visibilities, it is not possible to go back to PHP 8.3 or older. The error message is actually intriguing: Yes, PHP doesn't allow the multiplication of `private private private $property` and tell you about it. This check was introduced in PHP 5.3: then, you could make properties really really private by repeating the keyword. That 'feature' was removed in modern versions of PHP, for the better of everyone. ## private(set), case insensitive and no space The final funny aspect of this new token is its syntax constraints. It is a PHP keyword, and as such, is case insensitive. Hugh, UPPERCASE KEYWORDS... On the other hand, it doesn't tolerate any space anywhere, or it yields a PHP 8.3 error message. Nothing difficult to remember. Yet, when one is accustomed to the white-space flexibility of the append operator, it is difficult not to regret it. ## A RFC worth reading There is a lot more to mention about the asymmetric visibility, in particular its relationship with [references](https://wiki.php.net/rfc/asymmetric-visibility-v2#references), [objects](https://wiki.php.net/rfc/asymmetric-visibility-v2#object_properties), and [arrays](https://wiki.php.net/rfc/asymmetric-visibility-v2#array_properties), and [readonly](https://wiki.php.net/rfc/asymmetric-visibility-v2#relationship_with_readonly), and, obviously, with the upcoming [property hooks](https://wiki.php.net/rfc/asymmetric-visibility-v2#interaction_with_property_hooks), and [inheritance](https://wiki.php.net/rfc/asymmetric-visibility-v2#inheritance), and [sentinel data](https://wiki.php.net/rfc/asymmetric-visibility-v2#readonly_cannot_use_a_custom_sentinel), etc. This is too long to write here, and also, the [RFC is extremely well written](https://wiki.php.net/rfc/asymmetric-visibility-v2) and detailled. Take a look at it, and then, thank [Ilija](https://phpc.social/@ilutov) and [Larry](https://phpc.social/@Crell) for the hard work they put in this new PHP gem. ## Until then, happy auditing! In the mean time, give a try to the new asymmetric visibility for PHP properties and its new syntax with your own [download of the PHP 8.4](https://www.php.net/archive/2024.php#2024-10-10-1), for testing purposes, or online with [3v4l.org](https://3v4l.org/#live) (select git.master as PHP 8.4 version). --- # More about the Override Attribute Source: https://www.exakat.io/more-about-the-override-attribute/ # A few extra steps with PHP's Override attribute The [Override](https://www.php.net/manual/en/class.override.php) attribute was introduced in PHP 8.3. It is a new attribute, that marks methods that must be overriding a parent's class method: one of the parent must define a method with the same name. When this is the case, all is fine. If no parent method is overwritten, then PHP emits a fatal error at compilation. In this article, we'll review a number of cases that diverge from the main definition of the Override attributes. This will help deal with seemingly unexpected behaviors, that are just normal PHP behaviors. ## Override attribute is free until PHP 8.2 The override attribute is supported in the PHP 8.3 engine. That means PHP ignores it altogether in PHP 8.0 to 8.2. Attributes are completely ignored in PHP 7.4 and less. This is the expected behavior. If a PHP doesn't use Override, static analysis tools may take advantage of it. Although Exakat is currently the only one that has been found to use of it with older PHP versions. ## Anticipating Override Attribute for PHP 8.3 When the code is anticipating the move to PHP 8.3 and its subsequent usage of Override, it is possible to use the attribute in older PHP version, and let it activate with the new PHP 8.3 version. It is made easy by the nature of Override : it is mainly a compile-time check. This means that PHP won't compile with code that doesn't comply with the Override features. Hence, it is recommended to add a compile check with PHP 8.3 (or more) as soon as the code uses the Override attribute. This means relying on PHP 8.3 to check if the attribute is well used, as the othe version won't do it. The illustration above is a case of valid code in PHP 8.2 and not in 8.3. ## Override error message works in every cases Note the error message : `Fatal error: y::goo() has #[\Override] attribute, but no matching parent method exists`. This works in two cases : when the parent doesn't exist (illustrated above); it also works when the parent exists and not the method. The same message works in both cases, although it makes the diagnostic one step furthere away. ## Override attribute for interfaces The error message in case of unsatisfied Override attribute mentions `parent`, not class. This is also another less known feature of the Override: it applies to the interfaces. In fact, interfaces may also have a parent, and extends it. Using an override in an interface forces the interface to have a parent, and override its method. This looks a bit excessive, since interfaces do not have any body and cannot change the parent's behavior. Yet, Override ensures that the parent interface contains a specific method, and that it was not removed without regards for the children. Besides, it is possible to alter the method's signature, while keeping it compatible with the parent method. ## Override for enum Enumerations may have methods, and they cannot have a parent. PHP doesn't accept this usage, and reports the error. In short, no Override for enums. ## Override for traits Traits may have methods, and they cannot have a parent. This time, PHP does accept this usage, and postpone any error reporting until the actual inclusion of the trait in a class or enum. When the trait is `used` in a class, PHP adds the trait's methods to the class method, and checks the Override method. Also, note that `use` keyword inside a trait doesn't generate a `parent`, so overriding a method in another trait with the current doesn't remove the Override attribute. ## Override as a compile time feature We started the article by mentionning that Override is mostly a compile time check. This is the case, with the exception of traits, and autoload. The check for Override is postponed until the trait is actually loaded into a class. This means that Override in a trait may wait until execution time to produce an error. # Override small print In the end, here is a list of extra details to keep in mind when using Override : - When running on PHP 8.2 or older, add a compile check of the code with PHP 8.3 or your favorite static analysis to prepare for PHP 8.3 migration - Override works on interfaces - Override cannot be used in enumerations - Override is silent in traits, until actual usage, at execution time --- # 5 invisible bugs starring at you Source: https://www.exakat.io/5-invisible-bugs-starring/ I'm sure you have experienced the ever-green art of steganography : a bug hiding in plain sight in your code. Yet, this is an invisible bug. The code compiles, IDE reports nothing wrong, unit tests are a failing everywhere, and you keep reading the sources without finding anything. And suddenly, it strikes you : YES, there ! One hour lost due to a stupid typo. Even with experience dating back from the last millenium, I run occasionally into those. So, here is a list of five of those horrendous errors that you may encounter in your code. I collected them as a reference : the list often sparks inspiration and help me target my diagnostic efforts to the right place in the code. May it help you like it helped me. - The variable inside a string - The cutting semi-colon - The hidden iffectation - The nested comparison - The last line effect ## The variable inside a string Note that the contrary may very well occur too : the variable is shortened in the string. Here, the variable was longer ($urls['site']['http']), and pushing it into a double-quoted string makes it ignore the second index. PHP only access one level arrays inside a double-quoted string. The same is possible with the object notation and the properties. This case is related to refactor from concatenation to double-quote strings or HEREDOC strings. Static analysis is able to spot errors like that in the code. PHP variables are easy to identify, with their leading $. But this may easily collide with real-world notation, like countries that use $CAD as a currently indicator. ## The cutting semi-colon A sneaky semi-colon after a conditional statement is always hard to find : Note the hidden semi-colon, after the for : it actually void the loop, and the next instruction is only executed once. Always using curly brackets may help preventing this problem, though it is still possible : Finally, this may affect a lot of conditional structures : for, foreach, if/then/elseif. Static analysis is a good help here : empty blocks are rarely useful, and may be easily reported to manual inspection. ## The hidden iffectation PHP allows for iffectation : the ability to assign a value, and use it for comparison. For example : $file receives the value read from the directory $dir. The same value is used directly by while() to keep on running the loop, until readdir() return null or false, which reports an error or the end of the file list. Problems arise when the same ability is used with a constant, like the example below. The comparison is now constant too, and is always true (or false, depending on the constant), leading to a useless condition, and a modified variable. This may be avoided by using the Yoda comparison : set the constant on the left, so that no assignation may ever occur. In fact, in case of typo, PHP will not lint the code, and respond with a fatal error. ## The nested comparison Another vicious error is the comparison that goes inside. As brought to my attention by [Vladimir Reznichenko](https://twitter.com/kalessil/status/856597126767403010), a misplaced parenthesis may very well lead to valid PHP code : Here, the comparison should have been done with the result of trim(), not as an argument of trim, which only accepts strings (actually, boolean are cast to string first). Again, this leads to an strange behavior. One aspect that makes this error hard to find is that using a comparison as a parameter is valid in many situations : Those errors may be reported when the last argument used in the function is not supposed to be a boolean : for this, typehint may help. ## The last line effect The last line effect is a micro-cloning problem : copy-paste is not your friend. It is easy to understand on the example below : Obviously, the three lines are the same, and the two last are probably build by copy/paste/adaptation. Except that the alteration was not complete, and the last assignation is actually a bug : $z should get the z coordinate, instead of the mistaken y. Anytime there is a micro-clone, you may encounter those mistake. They are even harder to spot, as our brain is wired to skim over those similar looking blocks of code, and skip a full check. Coding conventions help with those. Static analysis is trying to find algorithms to spot the problem, which is quite spread : Read 'the last line effect explained' from [Moritz Beller](https://link.springer.com/article/10.1007/s10664-016-9489-6). ## Don't let the code source bug bite ! This list of bugs may very well be committed in the repositories. They may make their way past unit tests, though quite rarely. They may linger in the code for long times before being found by code review or static analysis. Indeed, all those errors are now treated by static analysis. The automated code review, like the [exakat](https://www.exakat.io/) engine, signals suspicious code, and target developper attention right in the files, on the line, where suspicious code is. It helps having a disciplined helper that can read code without getting used to read it again and again. Do you more than those five invisible bugs? Ping me on [Twitter](http://www.twitter.com/exakat). --- # All String Concatenations in PHP Source: https://www.exakat.io/all-string-concatenations-in-php/ ## All String Concatenations in PHP In PHP, string concatenation is a common operation used to combine multiple strings into a single one. There are no less than 4 ways to combine several strings into one. Each have it own usage, adapted to each context, and it is a good coding skill to know them all. In this blog post, we'll explore all the string concatenation in PHP, including the dot operator, the short assignment operator `.=`, string interpolation, and the implode() function. ### Dot Operator . The dot operator `.` is the most straightforward way to concatenate strings in PHP. It simply joins two strings together, creating a new string containing the combined text. The `.` operator may very well be chained to combine several values together. ### Short Assignment Operator .= The short assignment operator `.=` is a special case of the dot operator. It appends the right operand to the left one. Note that the right operand might also be a concatenation itself: in that case, the result of the whole concatenation on the right is appended to the variable. There is no operator to prepend strings: that is, to add a string at the beginning of the left operand. The workaround is to use the concatenation operator, and repeat the variable. ### String Interpolation String interpolation allows embedding variables directly within double-quoted strings. This feature simplifies string concatenation by automatically evaluating variables within the string. This applies to variables, properties and arrays (although, up to one level only). String interpolation helps reduce the amount of dot operator usage, by mixing variables and literal strings. On the other hand, some structures become quickly difficult to read. ### implode() Function The implode() function is primarily used to join all the array elements into a single string, by concatenation. It also features a glue, which is a string that is automatically added between the elements. Use the empty string when a simple concatenation is needed: otherwise, use a separator. implode() is really useful when an arbitrary number of strings are collected in an array, to be later turned into a string. Instead of doing as many concatenation as there are elements in the array (minus one), implode executes the operation in one take, and saves memory and execution cycles. ### Using str_replace() for Simple Template Systems One lesser known approach to concatenation is to set up a simple Template system with [str_replace()](https://www.php.net/str_replace). With such system, a template string is build with placeholders, that are replaced later by a dynamic value. In simple template systems, you may use placeholders in a template string and replace them with dynamic content. The str_replace() function can be handy for this purpose. str_replace() is able to execute multiple replacements at the same time, making the operation quite efficient. PHP has several other functions that can be used in this context : [preg_replace()](https://www.php.net/preg_replace), for replacement based on regex; [preg_replace_callback()](https://www.php.net/preg_replace_callback), for replacement with complex calculations; [strtr()](https://www.php.net/strtr) for single character replacements. This approach has been refined over the years into full blown templating engines, such as [Twig](https://twig.symfony.com/), [blade](https://laravel.com/docs/10.x/blade), [plates](https://platesphp.com/), etc. They are much more powerful, but also more resource hungry. Sometimes, one only need only a simple concatenation. ## Concatenating all strings PHP offers several methods for concatenating strings, each with its own use cases. The dot operator and string interpolation are the PHP documentation approaches; implode() and `.=` are dependent on the context and str_replace() is usually the last step before moving toward a full templating system. Experiment with these methods to find the ones that best suit your coding style and project requirements. Happy coding! --- Generated from RankReady