Revisiting ‘PHP: A Fractal of Bad Design’ — With Context



This content originally appeared on DEV Community and was authored by Tanzim Ibthesam

A long time back in 2012 and article titled Php-a-fractal-of-bad-design made a lot of noise across the development community. The link is attached if you want you can go through the article. Like it or not lot of things were actually true.
Thirteen years later, both the technological landscape and PHP itself have evolved dramatically. The criticisms were harsh but I found the original article extremely helpful for learning.
This article is not a counter argument. PHP has made significant strides specially since PHP 8.0 though some points from original article still resonate today.
I will point out the things and compare. As of now i have yet to cover everything this might be done in 2 parts or maybe the article might be edited.
Philosophy
1. PHP was originally designed explicitly for non-programmers (and, reading between the lines, non-programs); it has not well escaped its roots. A choice quote from the PHP 2.0 documentation, regarding + and friends doing type conversion:Once you start having separate operators for each type you start making the language much more complex. ie. you can’t use ‘==’ for stings [sic], you now would use ‘eq’. I don’t see the point, especially for something like PHP where most of the scripts will be rather simple and in most cases written by non-programmers who want a language with a basic logical syntax that doesn’t have too high a learning curve.Once you start having separate operators for each type you start making the language much more complex. ie. you can’t use ‘==’ for stings [sic], you now would use ‘eq’. I don’t see the point, epecially for something like PHP where most of the scripts will be rather simple and in most cases written by non-programmers who want a language with a basic logical syntax that doesn’t have too high a learning curve.

Here i will just say it was not intended to be a programming language in first place. The creator of PHP Rasmus Leordoff created a simple CGI wrapper in Perl to track his online resume visits in a preloaded server.
It was rewritten in C to improve performance.
In first place it was not intended for public use.Others discovered it than it grew to Personal Home Page.
Lerdorf developed “FI” (Form Interpreter) to embed SQL queries directly into web pages.

In 1995, this was combined with other components to create PHP/FI 2.0, which was described as “a complete rewrite of the two packages merged into a single application.”

Due to its simplicity and less learning curve it started gaining a lot of traction.

2.PHP is built to keep chugging along at all costs. When faced with either doing something nonsensical or aborting with an error, it will do something nonsensical. Anything is better than nothing.
It was not built for that no software i feel is ever is built for that. Since it wasnt intended to be a programming language at first place some core principles might not been properly dealt with but throughout time a lot of issues have been fixed,though not all but still its not the same language as it was when this article was written.

3.There’s no clear design philosophy. Early PHP was inspired by Perl; the huge stdlib with “out” params is from C; the OO parts are designed like C++ and Java.
But modern PHP has improved a lot
Since PHP 5.3+ and especially with PHP 7+, the language has matured significantly:
Strict typing (via declare(strict_types=1))
Better OOP (namespaces, traits, proper exceptions)
Performance improvements (Zend Engine, OPcache)
A more consistent standard library (thanks to frameworks like Laravel and tools like Symfony)
The core team now treats PHP more like a real programming language—with RFCs, proposals, and thoughtful design.

4.PHP takes vast amounts of inspiration from other languages, yet still manages to be incomprehensible to anyone who knows those languages. (int) looks like C, but int doesn’t exist. Namespaces use . The new array syntax results in [key => value], unique among every language with hash literals.
Now lets talk about technical know how
Int is a data type in C since PHP is not a strictly typed language its used to mainly typecast.As of PHP 7.0 or so you can pass argument types.

function int_type(int $a): int {
    return $a;
}

echo int_type(a: 50);

Still a lot different like C which use

#include <stdio.h>

int main() {
    int age = 30;

    // Print the value
    printf("Hello, I am %d years old.\n", age);

    return 0;
}

You can clearly use int as a data type in C.
Namespaces were introduced in PHP 5.3
For namespace \ is used lets say you have a folder directory App\Models and have a class so to import a namespace you will use App\Models\User;
Its understandable but in languages like C# or Java its much more simplistic
In C#
namespace App.Models
In java
package app.models;

Yes in PHP => is used for storing values in array whereas other languages use : colon.Even fat arrow functions have the same thing.

 $multiplied = array_map(fn($x) => $x * 2, [1, 2, 3, 4]);
// Result: [2, 4, 6, 8]int main() {

5.Weak typing (i.e., silent automatic conversion between strings/numbers/et al) is so complex that whatever minor programmer effort is saved is by no means worth it.

Yes PHP is not a strictly typed language as mentioned previously From PHP 7.0 you can pass types to arguments.with declare(strict_types=1):

declare(strict_types=1);

function add(int $a,int $b){
    return $a+$b;
}

print add(10,20);
?>

There is still a problem lets say you declare variables inside a function which is not a parameter lets say

$age=45; if you write
$age='age';
var_dump($age);

It will output string. Is there any way to prevent this ?Yes but not first party. There is something called PHPstan.

/** @var int $age */
$age = 45;
$age = 'age'; // PHPStan will flag this as a type error


Though code will execute PHP will not provide run time error but you can have an idea about declared variable type assigment issue if any.
6.There is a whole lot of action at a distance. Consider this code, taken from the PHP docs somewhere.
@fopen(‘http://example.com/not-existing-file‘, ‘r’);
What will it do?
If PHP was compiled with –disable-url-fopen-wrapper, it won’t work. (Docs don’t say what “won’t work” means; returns null, throws exception?) Note that this flag was removed in PHP 5.2.5.
If allow_url_fopen is disabled in php.ini, this still won’t work. (How? No idea.)
Because of the @, the warning about the non-existent file won’t be printed.
But it will be printed if scream.enabled is set in php.ini.
Or if scream.enabled is set manually with ini_set.
But not if the right error_reporting level isn’t set.
If it is printed, exactly where it goes depends on display_errors, again in php.ini. Or ini_set.

Ok its a huge statement lets break this part by part.
Consider this code, taken from the PHP docs somewhere.
@fopen(‘http://example.com/not-existing-file‘, ‘r’);

What it will do
If PHP was compiled with –disable-url-fopen-wrapper, it won’t work. (Docs don’t say what “won’t work” means; returns null, throws exception?) Note that this flag was removed in PHP 5.2.5.
So now let us at first study the function. Fopen
fopen(
string $filename,
string $mode,
bool $use_include_path = false,
?resource $context = null
): resource|false

The fopen takes many arguments but most important is filename and mode and filename usually refers to the name of file mode mode refers to things as read write r w.
Return value: On success, fopen returns a file resource. On failure, it returns false and typically triggers a warning.

–disable-url-fopen-wrapper completely disables fopen-wrapper
The stream wrappers like http,https and ftp were not built to PHP at all.
You cannot open any URL via fopen(), file_get_contents(),
If even the wrapper was disabled then it would still trigger a warning.
In modern PHP (e.g., 5.x and later), this has been replaced by the runtime php.ini directive allow_url_fopen, which can be toggled on or off without recompiling.
If allow_url_fopen is disabled (set to 0 or false in php.ini):
If its set to false than fopen cannot read any url.
It allows you to enable or disable URL-based stream wrappers dynamically without recompiling PHP.
Because of @ the warning of non existent file doesnt get printed.
To print it you need to enable scream.ini previously it used to
f scream.enabled is 1, the warning is not suppressed by @.
But not if the right error_reporting level isn’t set.
If it is printed, exactly where it goes depends on display_errors, again in php.ini. Or ini_set.
Yes so if you set error levels to
error_reporting(E_ALL & ~E_WARNING);
Its all fine.

In easy terms for allow_url_fopen you dont need to recompile if you make changes this is its main difference with –disable-url-fopen-wrapper.
–disable-url-fopen-wrapper is a compile-time flag whereas allow_url_fopen is a runtime configuration in php.ini.

7.The language is full of global and implicit state. mbstring uses a global character set. func_get_arg and friends look like regular functions, but operate on the currently-executing function. Error/exception handling have global defaults. register_tick_function sets a global function to run every tick—what?!

<?php
mb_internal_encoding('SJISK');
$mb_strlen=mb_strlen("café");
var_dump($mb_strlen);

If you set internal coding to anything other than UTF-8 prior to PHP 8 it would issue a warning now it triggers a fatal error.
Prior to Php 5.6 to find out total number of arguments you need you to use func_get_args

<?php
function foo()
{
    $numargs = func_num_args();
    echo "Number of arguments: $numargs \n";
    if ($numargs >= 2) {
        echo "Second argument is: " . func_get_arg(1) . "\n";
    }
    $arg_list = func_get_args();
     var_dump($arg_list);
}
foo(1, 2, 3);

But now splat operator is introduced from Php 5.6

<?php
function foo(...$args){
    //Total number of rags
    var_dump($args);
    //Getting access to first index
    var_dump($args[1]);
}
foo(1, 2, 3);

register_tick_function sets a global function to run every tick—what?!
register_tick_function() — along with the ticks directive — is a largely obsolete and rarely used feature in modern PHP,
If we define tick we can say that an event that occurs after a certain number of low level statements are executed.
You needed to enable tick

<?php
declare(ticks=1);
register_tick_function(function () {
    echo "Tick occurred\n";
});

for ($i = 0; $i < 3; $i++) {
    echo "Loop $i\n";
}

The tick function is mostly obsolete since it adds a significant overhead.
There are modern alternatives
pcntl_signal() + pcntl_signal_dispatch() — for signal handling (in CLI).
set_time_limit() / ini_set(‘max_execution_time’) — for timeouts.

Register_tick_function is not deprecated bus discouraged in the docs.

8.There is no threading support whatsoever. (Not surprising, given the above.) Combined with the lack of built-in fork (mentioned below), this makes parallel programming extremely difficult.
Yes till date there is no official support for multi threading in PHP.

9.Parts of PHP are practically designed to produce buggy code.
json_decode returns null for invalid input, even though null is also a perfectly valid object for JSON to decode to—this function is completely unreliable unless you also call json_last_error every time you use it.
array_search, strpos, and similar functions return 0 if they find the needle at position zero, but false if they don’t find it at all.
Let me expand on that last part a bit.
In C, functions like strpos return -1 if the item isn’t found. If you don’t check for that case and try to use that as an index, you’ll hit junk memory and your program will blow up. (Probably. It’s C. Who the fuck knows. I’m sure there are tools for this, at least.)

Lets first talk about json_encode and decode
If we see this example of json_decode
json_decode(
string $json,
?bool $associative = null,
int $depth = 512,
int $flags = 0
): mixed

json_decode takes a json encoded string and convert it to a PHp value.
If we look at the example below

<?php
$json="Not a json";
$json_decode=json_decode(json:$json);
var_dump($json_decode);

In Javascript

let json_string = "json_string";
console.log(JSON.parse(json_string));
// SyntaxError: Unexpected token 'j', "json_string" is not valid JSON

In Python

import json
json_string = "json_string"
parsed = json.loads(json_string)  # ❌ Raises error

So we see in both cases there even though its a string it doesnt return a null they both return error.
json_validate a new function perfectly works from PHP 8.3.Remember it will only work from PHP 8.3
If we look at json_validate function
json_validate(string $json, int $depth = 512, int $flags = 0): bool

<?php

$json_string = "\"string\""; // Properly quoted
$json_validate = json_validate(json: $json_string);
var_dump($json_validate); // Output: bool(true)
$decoded = json_decode($json_string);
var_dump($decoded); // Output: string(6) "string"
//Shorter syntax
$json = "\"string\"";
// Properly quoted
$data = json_validate($json) ? json_decode(json:$json, associative: true) : '';
var_dump($data);

10.array_search, strpos, and similar functions return 0 if they find the needle at position zero, but false if they don’t find it at all.
Lets see both the functions first
array_search(mixed $needle, array $haystack, bool $strict = false): int|string|false
strpos(string $haystack, string $needle, int $offset = 0): int|false
Here in both cases if needle is found at 0 it will return 0

<?php
$colors = array("red", "green", "blue", "red");
// Search for "green" (loose comparison by default)
$key1 = array_search(needle:"red", haystack:$colors);
var_dump($key1);
$string="string";
$strpos=strpos(needle:'s',haystack:$string);
var_dump($strpos);

So what is the problem.The problem is if no match is found it returns a false.
Lets say if you do comparison between both arrays and type 0==false it will return true. So just an easy option is to use === what its said instead if -1 was returned in that case it was an easy option.

Operators
1.== is useless.
It’s not transitive. “foo” == TRUE, and “foo” == 0… but, of course, TRUE != 0.
*
If we see

<?php
var_dump("foo" == TRUE);//true
var_dump( "foo" == 0);//true 
var_dump(0==false);//true

If you do something like this you see the inconsistencies foo is equivalent to 0 and on other hand foo equivalent to true and 0 is equivalent to false.
Its always recommended to use === for type juggling.

2.== converts to numbers when possible (123 == “123foo”… although “123” != “123foo”), which means it converts to floats when possible. So large hex strings (like, say, password hashes) may occasionally compare true when they’re not. Even JavaScript doesn’t do this.

From Php 8.0 this syntax returns false

<?php
var_dump(123=="123foo");

For comparing hash using == or even === is not the best practice

<?php
var_dump("500000000000000000001" == "500000000000000000002");//

Prior to PHP 5.4 it would return true. For comparing hashes you should not use double equals or even triple equals just use hash_equals
For comparison using hash_equals

<?php
$input = "secret";
$expected_md5 = md5($input); // e.g., stored hash

$user_input = "secret";
$user_md5 = md5($user_input);

// Safe comparison
if (hash_equals($expected_md5, $user_md5)) {
    echo "Match!";
} else {
    echo "No match.";
}

3.=== compares values and type… except with objects, where === is only true if both operands are actually the same object! For objects, == compares both value (of every attribute) and type, which is what === does for ev*
var_dump(133 === 0133);//false

var_dump(133 !== 0133);//

This is really weird 0133 is an actual value when you do a strict comparison it is equal it is ok but do a strict not it is not.
There is no way to identify if a number is octal or not just you need to do some manual check get if first index of a number is 0 or not.

4.=== compares values and type… except with objects, where === is only true if both operands are actually the same object! For objects, == compares both value (of every attribute) and type, which is what === does for every other type

<?php
class User {
    public $name;
    public function __construct($name) {
        $this->name = $name;
    }
}

$user1 = new User("Alice");
$user2 = new User("Alice");

var_dump($user1===$user2);//false

Here you can pass reference using ampersand to make them equal or use another variable.

<?php
$user2=&$user1;

var_dump($user1===$user2);

5.Comparison isn’t much better.
It’s not even consistent: NULL < -1, and NULL == 0. Sorting is thus nondeterministic; it depends on the order in which the sort algorithm happens to compare elements.

<?php
var_dump(NULL < -1);//true

var_dump(NULL ===0);//false


There is a workaround.

<?php             
var_dump(NULL <=> -1);
$null_check=(NULL <=> -1);
var_dump(isset($null_check));//true
var_dump($null_check===true);//false

So if you use == in case of NULL===0 returns true

Another thing is that if we write var_dump(3 <= -1);//true in such a case strict comparison doesnt work in comparison operator incase of null

6.The comparison operators try to sort arrays, two different ways: first by length, then by elements. If they have the same number of elements but different sets of keys, though, they are incomparable.

So here we see length is same so if we do something like $a>$b.will return true since length are equal but keys in a are greater than that of b.if there are mixed greater and smaller keys like in first array number in one index is greater and in another

<?php 
$a = [100, 200, 300]; 
$b = [1, 30, 1];
 var_dump($a<$b); 

In case of associative arrays
This is where things get a bit hard

<?php
$array1 = [
    "name" => "Alice",
    "age" => 30,
    "city" => "Dhaka"
];

$array2 = [
    "username" => "alice123",
    "email" => "alice@example.com",
    "country" => "Bangladesh"
];
var_dump($array1 < $array2); // false
var_dump($array1 > $array2); // false

Comparing associative structures (arrays, maps, dicts, objects) with different keys is inherently ambiguous, and most languages either disallow it or leave it to the developer to define what “comparison” even means.
For comparison use array_diff() or usort()
7.Objects compare as greater than anything else… except other objects, which they are neither less than nor greater than.
If we see here

<?php
$obj = new stdClass();
$num = 42;
$str = "hello";

var_dump($obj > $num);   // true
var_dump($obj > $str);   // true
var_dump($obj < $str);   // false
//But comparison between objects 
$obj1 = new stdClass();
$obj2 = new stdClass();

var_dump($obj1 > $obj2); // false
var_dump($obj1 < $obj2); // false

Internally, PHP assigns a type precedence for comparisons:
object > array > string > number > null > boolean
If you need to compare objects meaningfully:
Implement a custom method like compareTo() or getSortKey()
Use usort() with explicit logic

8.For a more type-safe ==, we have ===. For a more type-safe <, we have… nothing. “123” < “0124”, always, no matter what you do. Casting doesn’t help, either

<?php
var_dump(123<=0124); //returns false.
var_dump((int)"123" <= (int)"0124"); 

If they both are in string
// true

123 is a decimal number but 0124 is octalnumber

But you can convert octal to a decimal using octdec

<?php
$octal_string = "0124";
$decimal = octdec($octal_string);
echo $decimal; // Output: 84

9.Despite the craziness above, and the explicit rejection of Perl’s pairs of string and numeric operators, PHP does not overload +. + is always addition, and . is always concatenation.
The syntax of Perl

my $x = "5";
my $y = "10";

print $x + $y;  # 15
print $x . $y;  # "510"

Equivalent php syntax

<?php
$x="5";
$y="10";
echo $x + $y;//15           /
echo $x . $y; // 510

Wee see here both Perl and Php have identical syntax and PHP here type corcerses string to an integer.

10.The [] indexing operator can also be spelled {}.

<?php 
$arr=[10,20,30];
echo $arr{0};

This now triggers a fatal error as of PHP 8.0
Fatal error: Array and string offset access syntax with curly braces is no longer supported in /home/user/scripts/code.php on line 8

11.[] can be used on any variable, not just strings and arrays. It returns null and issues no warning.

<?php 
$var = null;
echo $var[0]; // null, no warning

As of PHP 8.0 it issues a Warning
Warning: Trying to access array offset on null

12.[] cannot slice; it only retrieves individual elements.

Yes [] is only use for accessing indexes not slicing
13.foo()[0] is a syntax error. (Fixed in PHP 5.4.)
In versions prior to PHP 5.4 this used to trigger Parse error: syntax error, unexpected ‘[‘ it was fixed in 5.4

13.Unlike (literally!) every other language with a similar operator, ?: is left associative. So this:

<?php
$arg = 'T';
$vehicle = ( ( $arg == 'B' ) ? 'bus' :
             ( $arg == 'A' ) ? 'airplane' :
             ( $arg == 'T' ) ? 'train' :
             ( $arg == 'C' ) ? 'car' :
             ( $arg == 'H' ) ? 'horse' :
             'feet' );
echo $vehicle;

prints horse.
As of PHP 8.0 this would trigger a syntax error

Fatal error: Unparenthesized a ? b : c ? d : e is not supported. Use either (a ? b : c) ? d : e or a ? b : (c ? d : e)

A much better syntax with a cleaner syntax is of follows

<?php
$arg = 'T';
$vehicle = match ($arg) {
    'B' => 'bus',
    'A' => 'airplane',
    'T' => 'train',
    'C' => 'car',
    'H' => 'horse',
    default => 'feet',
};

print $vehicle;

Variables
1.There is no way to declare a variable. Variables that don’t exist are created with a null value when first used.

Lets break this statement
There is no way to define a variable. This statement is not correct what he tried to say is
is when you don’t assign any value to variable it still gives a null this is also true incase if we don’t declare a variable and try to dump it.

PHP automatically creates undefined variables with null values when first referenced, without requiring explicit declaration.

Print a variable without any name

<?php echo $my_name;

Warning: Undefined variable $my_name.A warning is triggered. But the problem lies when you try to do something like this

<?php  
$my_marks=10;

$my_marks+$bonus;

var_dump($my_marks);s

Ideally it should stop execution of script. So though quirky but issue is pretty much mangabale ideally it should throw a fatal error.you always need to use isset in PHP.

<?php 
if(isset($my_marks) && isset($bonus)){
  echo $my_marks+$bonus;
}else{
    echo "The addition value is not found";
}

A shorter and cleaner code

<?php 
echo isset($my_marks, $bonus) ? $my_marks + $bonus : "The addition value is not found";
//If you do $my_name; just like this and echo $my_name;
$my_name;
echo $my_name;

So it triggers an error undefined variable
Now in some case the problem might be even bigger

Lets take an example of Javascript

console.log(my_name);

If you do something like this it throws an error ReferenceError: my_name is not defined

let my_marks=10;
console.log(my_marks+bonus);
//ReferenceError: bonus is not defined

In javascript it says that bonus is not defined
I am just going to use a bit of code snippet from Go

package main
import "fmt"

func main() {
     fmt.Println(marks);
}
If you even do 
func main() {
     marks :=10;
}

It will say declared and not used: marks

So we see PHPs way of dealing with undefined variable is quirky but somewhat still manageable considering the fact it triggers notices.You should use isset for checking if variable is set,null or not when ever there is come conditional check or computation using variables.

2.Global variables need a global declaration before they can be used. This is a natural consequence of the above, so it would be perfectly reasonable, except that globals can’t even be read without an explicit declaration—PHP will quietly create a local with the same name, instead. I’m not aware of another language with similar scoping issues.

<?php
$my_name = 10; // Global variable

function profile(){
    var_dump($my_name);
}
profile();

It throws an error saying Warning: Undefined variable $my_name

Instead you have to use global variable

<?php
$my_name = 10; // Global variable

function profile(){
    global $my_name;
    var_dump($my_name);
}
profile();//int(10)

So explicitly global variable is not good
Say you later want to refactor your function into a method or move it into another file:
With globals, you’d need to restructure dependencies
With proper arguments or dependency injection, refactoring is straightforward.
There is a common workaround using & variable

This approach has a problem too. In PHP if you want you can reassign a value or redeclare both work the same way.Lets say by mistake you type $my_name=”Tanzim Ib”; before invoking a function.It also looks like a hack.So whats the solution.Assuming when you want to have a constant value.
A simple and common workaround for this is

<?php
$my_name ="Tanzim"; // Global variable

function profile(&$my_name){

    var_dump($my_name);
}


profile($my_name);

Pass-by-reference is mutable by design; a stray assignment ($my_name = “Tanzim Ib”;) could unintentionally propagate.
You can use

<?php
const my_name="Tanzim";
function profile(){

    echo my_name;
}
profile();

Note:This would trigger an error in PHp 5.2

3.There are no references. What PHP calls references are really aliases; there’s nothing that’s a step back, like Perl’s references, and there’s no pass-by-object identity like in Python.

Yes php doesnt pass by references like Javascript,Python. To invoke a behavior like Python or Javascript you need to pass value by &.Till now its a problem.
The references in PHP can be termed as symbol aliases.
If we just take example of javascript

const arr=[10,20,30];
const arr2=arr;

arr2[3]=55;
console.log(arr);//[ 10, 20, 30, 55 ]
console.log(arr2);//[ 10, 20, 30, 55 ]

Here we see when you bring a change of any of the array the value of other array changes.
In Php

<?php
$arr=[10,20,30];
$arr2=$arr;

$arr2[3]=55;
var_dump($arr);
var_dump($arr2);

We see we dont get the same behaviour we get in other languages
To get it you need to use ampersand

<?php
$arr=[10,20,30];
$arr2=&$arr;

$arr2[3]=55;
var_dump($arr);
var_dump($arr2);

So yes but if you just know this it should not be a problem.

4.Referenceness” infects a variable unlike anything else in the language. PHP is dynamically-typed, so variables generally have no type… except references, which adorn function definitions, variable syntax, and assignment. Once a variable is made a reference (which can happen anywhere), it’s stuck as a reference. There’s no obvious way to detect this and un-referencing requires nuking the variable entirely

Yes but i dont think you will ever need referencing a primitive. Still if you reference a variable;e you should unset it

<?php
$a=5;
$b=&$a;

var_dump($a);
$b=15;
var_dump($a);

Here we have made $b a reference There is no way you can find that $b is a reference of $a.
The only thing you can do is unset the variable.

<?php
$a=5;
$b=&$a;
unset($b);//You can't detach $b from $a without destroying $b.

5.A reference can be taken to a key that doesn’t exist within an undefined variable (which becomes an array). Using a non-existent array normally issues a notice, but this does not.

<?php
$a;
$b =& $a['key'];
var_dump($a);
Result:array(1) {
  ["key"]=>
  &NULL
}

It should at least trigger a notice.
Usually when we try to access offset of an array

<?php
$a;
echo $a['key'];:
// Warning: Undefined variable $a
// Warning: Trying to access array offset on value of type null

Bottomline is dont use & in php
PHP silently:
Cast $a into an array.
Created the key ‘key’.
Assigned it a reference to NULL

6.Constants are defined by a function call taking a string; before that, they don’t exist. (This may actually be a copy of Perl’s use constant behavior.)
The user defined contents are created using the define function so which consists of a string and value.
There is no existence of it until call executes at runtime.
echo my_name; When you try to do something like this it triggers a fatal error.

<?php
define('my_name','Tanzim');
//Workaround
if (defined('my_name')) {
    $value = constant('my_name');
} else {
    $value = 'default';
}

Short syntax

<?php
$value = defined('my_name') ? constant('my_name') : 'default';
An easier option
const my_name='Tanzim';

define() → runtime constant creation (name must be a string, can be dynamic)

const → compile-time constant creation (faster, but name must be known at parse time)

7.Variable names are case-sensitive. Function and class names are not. This includes method names, which makes camelCase a strange choice for naming.

<?php
function myName(){
    var_dump("Function");
}
myname();

Here we see that function names are not cases sensitive no you need to be very careful.Ideally it should trigger atleast a notice
But variables are case sensitive in php

<?php
$my_name="Tanzim";

echo $my_Name;

If we do something like this it will throw fatal error

4-Constructs

1.array() and a few dozen similar constructs are not functions. array on its own means nothing, $func = “array”; $func(); doesn’t work.

Yes there are many language constructs in PHP which look like a function isset(),count(),array() but all of these are actually language constructs.
So something like this

<?php
$func = "array";
$result=$func(10,20);
var_dump($result);

You get an error Fatal error: Uncaught Error: Call to undefined function array()
If all language constructs behaved the same way still it was ok but if you do something like this

<?php
$count = "count";
$result=$count([10,20,30]);

var_dump($result);//3 you get an output
This inconsistency is due to PHP’s internal distinction between constructs and functions, even though both can appear with parentheses.
This gives an output of 3 so we see these things are somewhat confusing.
To overcome such inconsistencies there is a workaround

<?php
if(function_exists($count)){
$result=$count([10,20,30]);
echo $result;
}else{
    echo "function does not exist";
}

If we use function_exists it helps us to overcome such confusions.
$array=”array”

<?php
if(function_exists($array)){
$result=$array([10,20,30]);
echo $result;
}else{
    echo "function does not exist";
}

Here we get proper output if the function does not exist.
At times a function may exist but its not callable.In such instances we will check whether there is existence of a function as well as the function is callable or not.

<?php
$array="array";
if(function_exists($array) && is_callable($array)){
$result=$array([10,20,30]);
echo $result;
}else{
    echo "function does not exist";
}

In many cases in production you may disable some functions like eval().exec() from php.ini
Only checking function exists might trigger fatal errors if the function is not callab;e.

2.Array unpacking can be done with the list($a, $b) = … operation. list() is function-like syntax just like array. I don’t know why this wasn’t given real dedicated syntax, or why the name is so obviously confusing.

The list syntax looks like this

<?php
$list=[10,20,30];
list($a,$b,$c)=$list;
echo $a;
A much cleaner approach is available now since Php 7.1
$list = [10, 20, 30];


[$a, $b, $c] = $list;

echo $a; //Outputs 10

With checks 
[$a, $b, $c] = isset($list) ? $list : [null, null, null];

echo $a; // Outputs: 10
With associative array

$user_profile = [
    'name' => 'John',
    'age' => 28,
    'profile' => 'Software developer with a passion for language design and programming paradigms.'
];

list('name'=>$name,'age'=>$age,'profile'=>$profile)=$user_profile;

echo $name;

Now as of Php 7.1.33 you can now destructure associative array without lists

You can just use

<?php
['name'=>$name,'age'=>$age,'profile'=>$profile]=$user_profile;
echo $name;

A much better approach including handling potential issues with checks

<?php
$user_profile = [
    'name' => 'John',
    'age' => 28,
    'profile' => 'Software developer with a passion for language design and programming paradigms.'
];



if(isset($user_profile) && !empty($user_profile) && is_array){
    ['name'=>$name,'age'=>$age,'profile'=>$profile]=$user_profile;
}else{
    echo "User profile variable doesnt exist or is empty";
}


// echo isset($name) ? $name : '';

echo $name ?? '';

A much better approach

<?php
$user_profile = [
    'name' => 'John',
    'age' => 28,
    'profile' => 'Software developer with a passion for language design and programming paradigms.'
];
$keys=array_keys($user_profile);
$name_key=trim('name');
$age_key=trim('age');
$profile_key=trim('profile');
$check_name_key=in_array(needle:$name_key,haystack:$keys);
$check_age_key=in_array(needle:$age_key,haystack:$keys);
$check_profile_key=in_array(needle:$profile_key,haystack:$keys);
if(isset($user_profile) && !empty($user_profile)){
if($check_name_key && $check_age_key && $check_profile_key){

    [$name_key=>$name,$age_key=>$age,$profile_key=>$profile]=$user_profile;
}   
}

echo $name . PHP_EOL;
echo $age . PHP_EOL;
echo $profile . PHP_EOL;

Remember named arguments would only work from PHP 8.0
?>

3.(int) is obviously designed to look like C, but it’s a single token; there’s nothing called int in the language. Try it: not only does var_dump(int) not work, it throws a parse error because the argument looks like the cast operator.

Ok int is actually a cast operator in PHP. Since PHP is not a statically typed language so you cant declare a variable with types.
In C int is a type specifier It can be used to declare variables,parameters and return types

int x = 42;      // variable declaration
int add(int a, int b) { // function declaration
    return a + b;
}

As of PHP 8.0 you can pass argument types and also return type in functions

<?php
declare(strict_types=1);

function sum(int $a, int $b): int {
    return $a + $b;
}

echo sum(a: 10, b: 20);
?>

So here you can pass int as argument.
PHP has no runtime type objects for primitives. You can’t store “the int type” in a variable. Types are a compile-time parser thing in PHP, not a runtime object.

But you can typecast a string using int.

var_dump((int)(“44”));
It will typecast it to 44.
Remember one thing if number has fraction and in a string if you typecast it to int it will be ceiled.

<?php 
var_dump((int)("44.50"));//Output 44

A better approach to typecasting to intval or float

Using intval() and floatval()

<?php
var_dump(intval("44.50"));
var_dump(floatval("44.50"));

4.(integer) is a synonym for (int). There’s also (bool)/(boolean) and (float)/(double)/(real).

Yes in PHP its kind of redundant lets say you use

<?php
$num="44";
$num_to_int=(int)($num);

var_dump($num_to_int);
$num_to_integer=(integer)($num);

var_dump($num_to_integer);

$num="44";
$num_to_float=(float)($num);

var_dump($num_to_float);
$num_to_double=(double)($num_to_double);
var_dump($num_to_double);
<?php
$is_logged_in="hello";
 $convert_to_boolean=(boolean)($is_logged_in);
var_dump($convert_to_boolean);
$is_logged_in="hello";
 $convert_to_bool=(bool)($is_logged_in);
var_dump($convert_to_bool);

So we see all of these are redundant and can create confusion but there should we a convention followed incase you work in a team use either int or integer,is case of float either float or double there should be uniformity.

Choose one form per type and enforce it via tools like PHP_CodeSniffer or a style guide (e.g., PSR-12). For example:
Always use (int) over (integer)—it’s shorter and more common in docs.
Prefer (bool) for booleans (modern and concise).
Stick to (float) for floats—avoid (real) entirely, as it’s outdated and could be removed in future major versions (though no plans as of PHP 9.0 previews).
5.There’s an (array) operator for casting to array and an (object) for casting to object. That sounds nuts, but there’s almost a use: you can use (array) to have a function argument that’s either a single item or a list, and treat it identically. Except you can’t do that reliably, because if someone passes a single object, casting it to an array will actually produce an array containing that object’s attributes. (Casting to object performs the reverse operation.)

Lets take first part of the statement
There’s an (array) operator for casting to array and an (object) for casting to object. That sounds nuts, but there’s almost a use: you can use (array) to have a function argument that’s either a single item or a list

Talking about array operator.

Ok so what it said is

<?php
function arr($arr){
    foreach(array($arr) as $a){
        var_dump($a);
    }
}
arr('Tanzim');//Tanzim

Instead of array if i pass it as a string and typecast it to an array it will produce similar results.Problem is that since its iterating it should not be allowed to pass a string even though intentional typecasting is done.So what should be done?
Workaround
From Php 7.0 onwards you can set parameter types

<?php
declare(strict_types=1);
function arr(array $arr):void{
    foreach(array($arr) as $a){
        var_dump($a);
    }
}
arr('Tanzim');//Tanzim
?>

So here it will throw a fatal error saying Fatal error: Uncaught TypeError: arr(): Argument #1 ($arr) must be of type array, string given.
With strict_types=1, PHP throws a TypeError if you pass anything but an array (e.g., a string like ‘Tanzim’). This prevents the “intentional but risky” casting .
After Php 8.0 and onwards you can use union operator

<?php
declare(strict_types=1);
function arr(array|string $arr):void{
     $items = is_string($arr) ? [$arr]: $items;


    foreach($items as $a){
        var_dump($a);
    }
}
arr('Tanzim');//Tanzim

So this issue has proper workarounds as of now.

Lets now talk about casting array to object and object to array

<?php
declare(strict_types=1);
class User {
    public $name = "Alice";
    protected $age = 30;
    private $secret = "x";
}

$user = new User();
$items = (array) $user;

If we var_dump items and there are all sorts of properties public,private and protected so it doesnt give keys accurately.
If we var_dump($user) the output we get is
array(3) {
// [“name”]=>
// string(5) “Alice”
// [“�*�age”]=>
// int(30)
// [“�User�secret”]=>
// string(1) “x”
// }

//typecasting it to an array
//we see that the keys which are not public produce outputs that are not same as the casted object
So there is a workaround you can write a trait if you need such a regular behaviour.

<?php
$reflection=new ReflectionClass(User::class);
$properties=$reflection->getProperties();
$encoded_json_key_values=json_encode($properties);
 $decoded_json_key_values=json_decode($encoded_json_key_values,true);
var_dump("Key of classes");
$key_of_classes=array_column($decoded_json_key_values,'name');
var_dump($key_of_classes);
$value_of_classes=array_values($items);
$object_to_array=array_combine($key_of_classes,$value_of_classes);
var_dump($object_to_array);

6.include() and friends are basically C’s #include: they dump another source file into yours. There is no module system, even for PHP code.
The include require require_once are still part of the PHP and they will remain.

Lets say if you want to have a file like in index.php you want to import your Database.php file from index.php

In index.php we would most like likely write index.php
Ideally a path like this

<?php
include '.../../src/config/Database.php';

So for every file you would have to do some thing but as of 2014 PSr4 came out through you can do composer autoloading.

PSR-4 brought a convention-based autoloading system that mimics real module systems:
I am not discussing how to configure psr-4 but now you can just
Lets say in folder directory
src/Config/Database.php:

<?php
namespace App\Config;

class Database {
  // connection logic
}

In index.php

<?php
require 'vendor/autoload.php';

use App\Config\Database;

$db = new Database();

So simply it is a shift from file-based scripting to module based scripting

7.There’s no such thing as a nested or locally-scoped function or class. They’re only global. Including a file dumps its variables into the current function’s scope (and gives the file access to your variables), but dumps functions and classes into global scope.

If we look at first part of the statement
There’s no such thing as a nested or locally-scoped function or class.

<?php
function outer() {
    function helper() {
        return "I'm global!";
    }
}

outer();        // This defines `helper()` globally
echo helper();  // ✅ Works! `helper()` is now available everywhere
outer();        // ❌ FATAL ERROR: Cannot redeclare helper()
?>

Here if you write a function inside a function you can invoke that function anywhere.Here you can easily invoke helper. But when try to invoke outer() function again you can see that it gives an error that you cant redeclare it.
When outer() is called for the first time, PHP registers helper() globally.
When outer() is called again, PHP tries to redeclare helper() — and since functions can’t be redefined, you get a fatal error.

A work around for this you can use closures

<?php
function outer() {
    $helper = function() {
        return "I'm scoped!";
    };
    return $helper;
}

$fn = outer();
echo $fn(); // ✅ Works, scoped and reusable

8.Appending to an array is done with $foo[] = $bar.
Yes its if you have an array

<?php
$arr=[10,20,30,40];
$arr[]=50;
var_dump($arr);

So the output is 10 20 30 40 50
Instead we should use array_push

<?php
array_push($arr,50);
var_dump($arr);

Or we can also use

<?php
$arr=[...$arr,50];

$array=[...$arr,50];

var_dump($array);

In Python and Js it would throw an error

arr=[10,20,30,40]
arr[]=55
print(arr);

Though using $arr[] might be faster but using array_push or destructuring might be easier to understand.

9.echo is a statement-y kind of thing, not a function.

In PHP echo is a language construct not a built in function.
echo $strlen(“Hello”); You still get proper results. So you need to be careful which is a language construct and which is a built in function in PHP echo is a language construct not a function

<?php
echo "Hello World!";  // No parentheses needed
echo("Hello World!"); // Parentheses work but don't make it a function
?>

Both of them work. Though echo is not a function.

<?php
$echo="echo";

$echo("Hello");

It will throw an error.
But if you do something like this

<?php
$strlen="strlen";
echo  $strlen(abcd);
var_dump(function_exists('echo'));//false
var_dump(is_callable(echo));//false

10.empty($var) is so extremely not-a-function that anything but a variable, e.g. empty($var || $var2), is a parse error. Why on Earth does the parser need to know about empty? (Fixed in 5.5.)

<?php
$result = empty(isset($var) || isset($var2));
 echo $result;

In previous versions of PHP it would throw Parse error: syntax error, unexpected ‘isset'(T_ISSET)

11.There’s redundant syntax for blocks: if (…): … endif;, etc.
Yes if you use if else with curly braces you can see
// Standard syntax

<?php
if ($condition) {
echo "True";
} else {
echo "False";
}
// Alternative syntax
if ($condition):
echo "True";
else:
echo "False";
endif;

Without end if there is no way to find out where the if condition ends since you cant use curly brace.

Arrays

1.=> isn’t an operator. It’s a special construct that only exists inside array(…) and the foreach construct.=> is lot a standalone operator in PHP

Yes its absolutely true => is a language construct its only used inside a array or with foreach.All key value pairs inside arrays are stored with this and this is also used to when looping using foreach
So => is only for array and foreach.

2.Negative indexing doesn’t work, since -1 is just as valid a key as 0.
Php doesnt support negative indexing like Python even in Javascript negative indexing is not supported
In Python you can simply do

arr = ['a', 'b', 'c']
print(arr[-1])  # Output: 'c' — last element

With array_slice you can achieve the same behaviour.Lets say you want to access last element of an array

<?php
$nums=[10,20,30,40,50];

$sliced_array=array_slice($nums,-1);

var_dump($sliced_array);

Now if you want to get access to last 2 elements of an array

<?php
$nums=[10,20,30,40,50];

$sliced_array=array_slice($nums,-2);

var_dump($sliced_array);

3.Despite that this is the language’s only data structure, there is no shortcut syntax for it; array(…) is shortcut syntax. (PHP 5.4 is bringing “literals”, […].)

Array is not the only data structure that exists in PHP.PHP offers other data structure through PHPs standard PHP Library which was introduced in PHP 5.3. These include specialized classes like SplStack, SplQueue, SplHeap, SplFixedArray (for fixed-size arrays), and SplObjectStorage for object sets it also has a and Ds (Data Structures) extension:

If we come to this part of the statement we see.
there is no shortcut syntax for it; array(…) is shortcut syntax
You can now use … spread operator

<?php
$num1=[10,12];
$num2=[40,50];
$arr=[...$num1,...$num2];
var_dump($arr);

4.Similarly baffling, arrays stringify to Array with an E_NOTICE. is it still relevant in PHP

<?php
$nums=[10,20,30];

Yes if you do echo $nums; It will throws a notice or warning,warning mostly in versions after PHP 8.0
Warning: Array to string conversion in PHP

It is because echo is a language construct and it can only print scalar value types string,bool,float etc
You can do something like this

<?php
$nums = [10, 20, 30];
$nums = [10, 20, 30];
echo is_array($nums) ? print_r($nums, true) : 'It is not an array';

Other than debugging you wont need to print an array so use of var_dump or just using print_r pretty much does the job.

5.The => construct is based on Perl, which allows foo => 1 without quoting. (That is, in fact, why it exists in Perl; otherwise it’s just a comma.) In PHP, you can’t do this without getting a warning; it’s the only language in its niche that has no vetted way to create a hash without quoting string keys.

In Perl => is used as a syntactic sugar for comma, it auto-quotes barewords on the left-hand side. So foo => 1 is equivalent to ‘foo’, 1. This makes hash readable.**
An example using Perl

use strict;
use warnings;

my %hash = (foo => 1, bar => 2);

print %hash;

But in PHP

<?php
$arr=[
    foo=>1
    ];

    var_dump($arr);

If you do something in PHP you will get a fatal error in latest php versions
Fatal error: Uncaught Error: Undefined constant “foo”

In Javascript you store value in key value pairs inside an object you can do this the key does not need to be a string

let obj={
name:'myname'
}

console.log(obj);

In Python like PHP it would throw an error

data = {
    name: 'myname'
}

print(data);

It will throw an error
ERROR!
Traceback (most recent call last):
File “”, line 4, in
NameError: name ‘name’ is not defined

6.Array functions often have confusing or inconsistent behavior because they have to operate on lists, hashes, or maybe a combination of the two. Consider array_diff, which “computers the difference of arrays”.
$first = array(“foo” => 123, “bar” => 456);
$second = array(“foo” => 456, “bar” => 123);
echo var_dump(array_diff($first, $second));
What will this code do? If array_diff treats its arguments as hashes, then obviously these are different; the same keys have different values. If it treats them as lists, then they’re still different; the values are in the wrong order.
In fact array_diff considers these equal, because it treats them like sets: it compares only values, and ignores order.

Lets make it simple and see what array_diff does

<?php
$first  = array("foo" => 123, "bar" => 456);
$second = array("foo" => 456, "bar" => 123);

echo var_dump(array_diff($first, $second));
//output
array(0) {
}
If you want comparison between keys as of Php 5.4 there is a function called array_diff_key

<?php
$first = [
    "foo" => 123,
    "bar" => 456,
    "baz" => 100,
    "baz2" => 400
];

$second = [
    "foo" => 456,
    "bar" => 123
];

$result = array_diff_key($first, $second);
var_dump($result);

It simple gives the difference between 2 keys

7.In a similar vein, array_rand has the strange behavior of selecting random keys, which is not that helpful for the most common case of needing to pick from a list of choices.
array_rand gives us value of keys if we see there

<?php
$fruits = [
    "apple" => "red",
    "banana" => "yellow",
    "grape" => "purple",
    "orange" => "orange"
];

$randomKey = array_rand($fruits);
echo "Random key: $randomKey\n";
//To gain actual value you have to do this
echo "Value: " . $fruits[$randomKey] . "\n";

If you take an array which has elements of index array as well as associative arrays

<?php
$fruits = [
    "color" => "red",4,5
];

$randomKey = array_rand($fruits);
var_dump($randomKey);

Each time you run the code it will only print the keys.
If you want to print the value

<?php
$values=$fruits[$randomKey];
var_dump($values);

8.Despite how heavily PHP code relies on preserving key order:
array(“foo”, “bar”) != array(“bar”, “foo”)
array(“foo” => 1, “bar” => 2) == array(“bar” => 2, “foo” => 1)
I leave it to the reader to figure out what happens if the arrays are mixed. (I don’t know.)

The answer is simple just use === operator instead of ==

<?php
var_dump(["foo" => 1, "bar" => 2] === ["random" => 2, "foo" => 1]); // false
 var_dump(["foo", "bar"] === ["foo", "bar"])// true

While using === you can figure out
The good thing is var_dump([“foo” => 1, “bar” => 2] === [“foo” => 1, “bar” => 2]); here if you see now if same keys have different values than also it will be false

9.array_fill cannot create zero-length arrays; instead it will issue a warning and return false.
The purpose of array_fill is to fill an array with values

<?php
array_fill(int $start_index, int $count, mixed $value): array

$filled_array = array_fill(0, 0, ['key'=>'value']);
print_r($filled_array);

If we did this in versions prior to 5.6 we would get an error
In 5.4 we would get error
Warning: array_fill(): Number of elements must be positive
But as of PHP 5.6 it returns an empty array
Array
(
)
So this issue is solved now.

10.All of the (many…) sort functions operate in-place and return nothing. There is no way to create a new sorted copy; you have to copy the array yourself, then sort it, then use the array.
Ok so all sort function in PHP mutates the original array

If we simply look at the original sort function we
It clearly goes to the pointer
sort(array &$array, int $flags = SORT_REGULAR): true
so if we do

<?php
$nums=[10,20,30,40,50];
sort($nums);
var_dump($nums);

This thing mutates the original array
So to avoid this you can create a copy

<?php
$nums=[10,20,30,40,50];
$sortedNums=$nums;
sort($sortedNums);
var_dump($sortedNums);

11.But array_reverse returns a new array.
Yes if you look at this
array_reverse(array $array, bool $preserve_keys = false): array

If you look at this you see there is no reference before $array so it returns a new array.

12.A list of ordered things and some mapping of keys to values sounds kind of like a great way to handle function arguments, but no.
If we look athe the function below

<?php
function greet($name, $age) {
    echo "Hello $name, you're $age\n";
}

$args = ['Alice', 25];
greet(...$args); // ✅ Works! Output: Hello Alice, you're 25

You can even use it with associative arrays just ensure the names of keys are same as arguments passed

<?php
$args = ['name' => 'Alice', 'age' => 25];
greet(...$args); // ✅ Works! Output: Hello Alice, you're 25
function greet($name, $age) {
    echo "Hello $name, you're $age\n";
}

The code above works as of PHP 8.0 it would trigger a fatal error previously
Yes, function arguments can now work like lists and like maps, depending on how you call them.

Functions
If we look at the function below

<?php
function &getArray()
{
     static $arr = [1, 2, 3];
      return $arr;
}

$arr=&getArray();

array_push($arr,10);

print_r($arr);
var_dump(getArray());

So here its a reference returning function though using & is not at all recommended what happens here is the function itself is a referencing function and when you assign a reference things even get more complicated.
The static keyword ensures that array persists to multiple calls inside get array while maintaining its state
$arr becomes an alias (reference) to the static array inside the function. Any changes to $arr will directly affect the static array.

So arr will have all access to the states or variables inside the function and using functions like array_push,splice you can mutate the array inside function.

You should not use & Instead

<?php
function getArray() {
    $arr = [1, 2, 3];
    return $arr;  // Returns a copy
}

$arr = getArray();  // $arr is now a separate copy
array_push($arr, 10);

print_r($arr);      // Shows [1, 2, 3, 10]
var_dump(getArray());  

// Still shows original [1, 2, 3] — no side effects!

2.Function arguments can have “type hints”, which are basically just static typing. But you can’t require that an argument be an int or string or object or other “core” type, even though every builtin function uses this kind of typing, probably because int is not a thing in PHP. (See above about (int).) You also can’t use the special pseudo-type decorations used heavily by builtin functions: mixed, number, or callback. (callable is allowed as of PHP 5.4.)
PHP types have drastically changed since version 7.0
In version 5.4 or so PHP allowed argument types for only non scalar types like arrays,objects etc

<?php
function sum(int $a,int $b){
    return $a+$b;
}
 echo sum(10,10);
?>

This code would throw an error in PHP 5.4 saying that
Catchable fatal error: Argument 1 passed to sum() must be an instance of int, integer given
From PHP 7.0 you can type hint scalar types like int,string,bool,

As of PHP 8.0 you can also use mixed Every value is accepted by mixed its like a union type of all these object|resource|array|string|float|int|bool|null.

Its a different thing though whether you should use mixed or not.
There is no number type in PHP so you have to use either float or int.

3.You may notice that the “type hint” given doesn’t actually have to exist; there is no string class in this program. If you try to use ReflectionParameter::getClass() to examine the type hint dynamically, then it will balk that the class doesn’t exist, making it impossible to actually retrieve the class name.

ReflectionParameter::getClass() this method is deprecated as of PHP 8.0 so

There were issues with PHPs types in PHP 5.6

<?php
class User{

}

function processItem($item) {
    // Optional: Manual check if we expect a specific class
    if (is_object($item) && !($item instanceof NonExistentClass)) {
        die("Error: Item must be a NonExistentClass object!");
    }
    // Do something
    return "Processing item";
}

echo processItem(new User());

If you see this code you see as previously discussed there was no type hint for scalar types so lets say if you dont did not want object as parameter you needed to do checks inside but as of PHP 7 you can type hint parameters. So if you needed string or scalar it was a lot of hassle previously.

4.Function doesnt have return type.

As of PHP 7 you can return types in a function.

<?php
function add(int $a,int $b):int{

    return $a+$b;

}

echo add(20,30);

5.Passing the current function’s arguments to another function (dispatch, not uncommon) is done by call_user_func_array(‘other_function’, func_get_args()). But func_get_args throws a fatal error at runtime, complaining that it can’t be a function parameter. How and why is this even a type of error?
This would trigger error in Php 5.2 or so.

<?php
function dispatcher() {
    // This would fail with a fatal error in Php 5.2
    call_user_func_array('receiver', func_get_args());
}

function receiver($a, $b) {
    echo "Received: $a and $b";
}

dispatcher('hello', 'world');  // Fatal error here

In PHP 5.2 it would say
Fatal error: func_get_args(): Can’t be used as a function parameter
But from PHp 5.3 it has been fixed.

6.Closures require explicitly naming every variable to be closed-over. Why can’t the interpreter figure this out? Kind of hamstrings the whole feature. (Okay, it’s because using a variable ever, at all, creates it unless explicitly told otherwise.

This thing is still relevant now and i particularly dont like scoping in PHP.Lets say for an anonymous function you need to use variable inside scope of a function you always need use keyword

<?php
$userId=5;

$posts=function()use($userId){
     return $userId;
};

var_dump($posts());
?>

7.Because closed-over variables are effectively automatically-passed arguments and there are no nested scopes, a closure can’t refer to private methods, even if it’s defined inside a class. (Possibly fixed in 5.4? Unclear.)

This issue has been fixed in PHP 5.4

<?php
class Foo {
    private function secret() {
        return "secret value";
    }

      public function getClosure() {
        return function() {

            return $this->secret();
        };
    }
}

$foo = new Foo();
var_dump($foo->getClosure());

The code snippet would throw an error in PHP 5.2
Parse error: syntax error, unexpected T_FUNCTION in

As of PHP 5.6 or so you would get

<?php 
object(Closure)#2 (1) {
  ["this"]=>
  object(Foo)#1 (0) {
  }
}

PHP ≤ 5.2 – no anonymous functions → parse error.
PHP 5.3 – anonymous functions exist, but closures created inside a class aren’t bound; private access fails.
PHP 5.6 – automatic binding was added, fixing the issue; the closure above works unchanged in every current PHP release.

So as of Php 5.6 you get the object when you dump.

8.No named arguments to functions. Actually explicitly rejected by the devs because it “makes for messier code.
There are named arguments as of PHP 8.0 and they can be used for both built in functions and user defined functions
Named arguments provide you with the option of naming the arguments such that you dont have to remember the positions.
With built in functions

<?php
$array=[10,20,30,40];
The example below you can see
if(in_array(needle:20,haystack:$array)){
    echo "It is there";
}else{
    echo "Is it not there";
}

We can clearly now understand the needle which value is being searched and haystack and from where the value is being searched.
We know PHP has inconsistencies in assigning some of the built in functions like in some functions you need to provide needle at first and in some functions haystack as first. So lets look at example to be more clear

If you look at the functions below
strpos($haystack, $needle); // haystack first
array_search($needle, $haystack); // needle first
In these two built in functions you see in one function you need to put haystack first and in other function you need to put needle first. Ideally if you want to put needle first just use named arguments.

For a user defined function

<?php
function userProfile($name,$designation){
    return "His name is $name and designation is $designation";
}
echo userProfile(name:"Tanzim",profession:"Software Developer");

You see it has lot of clarity

9.Function arguments with defaults can appear before function arguments without, even though the documentation points out that this is both weird and useless. (So why allow it?)
Prior to PHP 8.0

<?php
function example($a = 'default', $b = null) {
    echo "a: $a, b: $b\n";
}

example(30); // 30

You already have a default value but when you assigned 30 it passed value to first argument the output is
a: 30, b:
So a is 30 and there is nothing printed after b since its null what if you want to assign a value to b only you have to do something like this
example(‘default’,30);
But with named arguments it is no more is the case

<?php
function example($a = 'default', $b = null) {
    echo "a: $a, b: $b\n";
}

 example(30); 

Now what you can do is

<?php
function example($a = 'default', $b = null) {
    echo "a: $a, b: $b\n";
}

 example(b:30); 

It will assign 30 to b
Output a:default b:30;

Also if you only want to assign value to a
example(a:30);

<?php
function example($a = 'default', $b = null) {
   echo "a:$a,b:$b";
}

example(a:30); // a: 30, b:

10.Extra arguments to a function are ignored (except with builtin functions, which raise an error). Missing arguments are assumed null.

<?php
function userFunc($a) {
    echo "Arg: $a";
}
userFunc(1, 2, 3);  // Outputs: Arg: 1 (2 and 3 ignored, no error)

This is still pretty much true and what you can just do is

Ok there is a way to check the total number of arguments and total arguments passed

<?php
function userFunc($a) {
    $totalPassed = func_num_args();

    // Use Reflection to get how many parameters the function DECLARES
    $reflection = new ReflectionFunction('userFunc');
    $totalExpected = $reflection->getNumberOfParameters();

    echo "Arguments passed: $totalPassed\n";     // → 3
    echo "Parameters expected: $totalExpected\n"; // → 1
}
userFunc(10, 20, 30);

But lets say even with a helper function or trait it seems an overkill. The same scenario kind of exists in Javascript

function userFunc(a,b,c){
    console.log(a,b,c)
}

userFunc(10);

11.Variadic functions require faffing about with func_num_args, func_get_arg, and func_get_args. There’s no syntax for such a thing.

Prior to PHP 5.6 for use of variadic functions you needed to use

<?php
  function sum(){
      $args=func_get_args();
       echo $sum;
}

You had to use func_get_args() to capture all passed arguments:

<?php
sum(10,20,30);

But since php 5.6 you can use splat operator as parameter which means if you pass arguments it will automatically be converted to array

<?php
 function sum(...$args){
    if(isset($args) && is_array($args) && !empty($args))
       {
         $sum=array_sum($args);
       }

}

sum(10,20,30);

Output

array(3) {
  [0]=>
  int(10)
  [1]=>
  int(20)
  [2]=>
  int(30)
}

For summing all elements in array we can get 
     echo array_sum($args);
}

sum(10,20,30);

The final output is 60.

Data Manipulation-Text

1.No Unicode support. Only ASCII will work reliably, really. There’s the mbstring extension, mentioned above, but it kinda blows.
To explain this i need to go a little bit back to fundamentals.First let me explain what is ASCII and Unicode
ASCII refers to the American Standard Code for Information Exchange. We know that what Computer can only understand 0 and 1 only binary. So whatever information needs to be processed by Computer image,texts etc will be converted to binary.

Something like $my_name=”John” while processing this variable or information contained in the variable “John” can be represented using ASCII values → converted to bytes → stored in binary.

But ASCII had limitations.its limited to 128 characters and it can only accept english A-Z a-z and numbers 1-9 basic punctuation, and control characters.
So lets say if you had to use any language other than English it would be problematic. Unicode was made to overcome limitations of ASCII.
Unfortunately PHP till date as of 2025 has no native support for unicode.
So lets say if you want to count some text in arabic,bengali or any other language if you use strlen it will give you accurate results.

<?php
$string = "Hello 😊 تنظيم";
$length=strlen($string); 

The problem with strlen is it only counts the bytes
So for English its ok but when you want to count length of any other languages it doesnt give accurate results.

<?php
  $char_count = mb_strlen($string, 'UTF-8');
var_dump($char_count);

So this is the way you will get the proper count
You can also use IntlChar
IntlChar is a built-in class in PHP (introduced in version 7.0)

2.Using the builtin string functions on UTF-8 text risks corrupting it.
The built in functions don’t give the correct output cause they only count the bytes so that’s why there needs to be use of mb_ functions while dealing with other languages since PHP has no native Unicode support like previously discussed

<?php

//Below you can see a text written in English vs any other language
$string = "আমার ";

// Byte-based operations (built-in functions)
$byte_length = strlen($string); // Counts bytes
$byte_substr = substr($string, 0, 3); // Takes first 10 bytes
$byte_split = str_split($string, 4); // Splits into 4-byte chunks

var_dump("Without mb functions");

var_dump($byte_length);
var_dump($byte_substr);
var_dump($byte_split);
// die();
// Character-based operations (mbstring)
$char_length = mb_strlen($string, 'UTF-8'); // Counts characters
$char_substr = mb_substr($string, 0, 3, 'UTF-8'); // Takes first 10 characters
$char_split = mb_str_split($string, 4, 'UTF-8'); // Splits into 4-character chunks

var_dump("With mb functions");

var_dump($char_length);
var_dump($char_substr);
var_dump($char_split);
?> 

3.Similarly, there’s no concept of e.g. case comparisons outside of ASCII. Despite the proliferation of case-insensitive versions of functions, not one of them will consider é equal to É.

The core string functions in PHP dont support case insensitive comparisons for non ASCII characters.

To compare Unicode strings case-insensitively you can use

<?php
$a = "é";
$b = "É";

var_dump(mb_strtolower($a, 'UTF-8') === mb_strtolower($b, 'UTF-8')); // true

You can even use Normalizer class

<?php
use Normalizer;

function unicode_case_compare($a, $b) {
    $a = Normalizer::normalize($a, Normalizer::FORM_C);
    $b = Normalizer::normalize($b, Normalizer::FORM_C);

    return mb_strtolower($a, 'UTF-8') === mb_strtolower($b, 'UTF-8');
}

var_dump(unicode_case_compare("é", "É")); // true

4.You can’t quote keys in variable interpolation, i.e., “$foo[‘key’]” is a syntax error. You can unquote it (which would generate a warning anywhere else!), or use ${…}/{$…}.

<?php
$foo=[
    'bar'=>'hello'
    ];
echo "The foo is $foo['bar']";.This throws a syntax error.
//Correct syntax is
echo "The foo is {$foo['bar']}";

5.”${foo[0]}” is okay. “${foo[0][0]}” is a syntax error. Putting the $ on the inside is fine with both. Bad copy of similar Perl syntax (with radically different semantics)?

<?php
$foo=[
    ['hello world','world'],['hello','foo']
    ];

echo "The foo is {$foo[0][0]}";

This works perfectly from PHP 5.4
Prior to PHP 5.4 it used to trigger an error
Parse error: syntax error, unexpected ‘[‘

Data Manipulation-Numbers

1.Integers are signed and 32-bit on 32-bit platforms. Unlike all of PHP’s contemporaries, there is no automatic bigint promotion. So you can end up with surprises like negative file sizes, and your math might work differently based on CPU architecture. Your only option for larger integers is to use the GMP or BC wrapper functions. (The developers have proposed adding a new, separate, 64-bit type. This is crazy.) isa it still true explain it a bit simply
First let me explain a bit about 32 bit and 64 bit
int (Integer)
Definition: A basic data type used to represent whole numbers (positive, negative, or zero).
Range
32-bit signed int: from -2,147,483,648 to 2,147,483,647
64-bit signed int: from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

Bigint

A data type that can represent arbitrarily large integers — limited only by available memory
It is used while working with very large numbers (e.g., cryptography, scientific calculations, factorial of large numbers).
If we look at first part of the statement
.Integers are signed and 32-bit on 32-bit platforms.
All integers in PHP are signed means they can accept a lange range of numbers from negative to positive.
So you can have a range below
-2,147,483,648 to 2,147,483,647,(i.e., -2^31 to 2^31 – 1)

If there is a number bigger than this it will convert that to float.
.
There is hardly any OS of 32 bit as of today and ideally in most cases you would not deal a number greater than this.
The range of a 64 bit integer is -2⁶³ to 2⁶³ – 1
If you need numbers larger than 9 quintillion, or exact precision, use:
BCMath (for decimal math)
GMP (for integer math)

<?php
$a = '900000000000000000000'; 
$b='1';
$c='2';
var_dump($a+$b);
$sum1= bcadd($a, $b);
$sum2=bcadd($a,$b);
$sum3=bcadd($a,$c);
$comp_one=bccomp($sum1,$sum2);

var_dump($comp_one===0);

$cmp2=bccomp($sum2,$sum3);

var_dump($cmp2===0);

If we need comparison we van use bccomp

2.PHP supports octal syntax with a leading 0, so e.g. 012 will be the number ten. However, 08 becomes the number zero. The 8 (or 9) and any following digits disappear. 01c is a syntax

<?php
echo octdec("012");//Output 10
echo octdec("008");
08 as of PHp 8.0 throws a deprecated error though it still runs and outputs 0
echo octdec("01c");

The code above throws a deprecation error.So for octal conversion you really need to be careful.

3.0×0+2 produces 4. The parser considers the 2 as both part of the hex literal and a separate decimal literal, treating this as 0x002 + 2. 0x0+0x2 displays the same problem. Strangely, 0x0 +2 is still 4, but 0x0+ 2 is correctly 2. (This is fixed in PHP 5.4. But it’s also re-broken in PHP 5.4, with the new 0b literal prefix: 0b0+1 produces

This problem no longer prevails

<?php
echo 0x0+2;
 echo 0x0 +2;
var_dump(0x002 + 2);

Now it correctly outputs

4.pi is a function. Or there’s a constant, M_PI.

<?php
echo pi(); // Outputs: 3.1415926535898
$pi="pi";

$exists_and_is_callable=function_exists($pi) && is_callable($pi);
if($exists_and_is_callable){
    echo $pi(); 
}

$m_pi = "M_PI"; // Not a function — a constant

// To get its value:

var_dump("pi from varaible function");
var_dump($pi()===$m_pi);


var_dump(pi()===M_PI);
?> 

Both can be used but using M_PI is often recommended for mathematical precision and accuracy.
5.There is no exponentiation operator, only the pow function.
pow function can be used but as of php 5.6 similar behaviour can be achieved using double asterik ** operator where base will be on left

<?php
$base = 2;
$exponent = 3;
$result = $base ** $exponent; // Calculates 2 raised to the power of 3 (2 * 2 * 2)
echo $result; // Output: 8

So this is it there are more parts which might be covered in a separate blog or maybe more points can be added in this blog. I have tried my best to explain things but if you have any feedback or constructive criticisms, please feel free to leave a comment below.


This content originally appeared on DEV Community and was authored by Tanzim Ibthesam