DEV Community

Cover image for State of JS 2025: Popular Syntax Features Explained with PHP Equivalents
Roberto B.
Roberto B.

Posted on

State of JS 2025: Popular Syntax Features Explained with PHP Equivalents

The State of JS is an annual developer survey that captures how thousands of JavaScript developers feel about the language, its ecosystem, and the features they use most, from syntax and tooling to frameworks and testing.

I was reading The State of JS 2025 survey and landed on the Syntax Features section. One question caught my attention:

Which of these syntax features have you used?

It made me curious. Not just why these features are popular in JavaScript, but also:

Do we have something similar in PHP?

So I turned this into a small personal exercise:

  • Understand the most-used/appreciated JavaScript syntax features
  • Look for their closest equivalents in PHP
  • Explain them with some examples

This article is the result of that cozy Saturday exploration.

A JavaScript to PHP walkthrough, written with curiosity rather than comparison wars.

1. Nullish Coalescing

Nullish coalescing allows you to provide a default value that is used only when a variable is truly missing (null or undefined).

JavaScript

Nullish Coalescing operator (??) provides a default value, but only when the original one is truly missing.

const someEmpty = null;
const foo = someEmpty ?? "default string";
console.log(foo); // "default string"
Enter fullscreen mode Exit fullscreen mode

Here, someEmpty is null, so JavaScript falls back to 'default string'.

const zeroValue = 0;
const baz = zeroValue ?? 42;
console.log(baz); // 0
Enter fullscreen mode Exit fullscreen mode

In this case, 0 is a perfectly valid value. Even though it’s falsy, it’s not null or undefined, so JavaScript keeps it.

That’s the real power of nullish coalescing: it lets you distinguish between "no value at all" and "a value that just happens to be falsy".

PHP Equivalent

PHP introduced the null coalescing operator in PHP 7.0, and it behaves almost identically.

$someEmpty = null;
$foo = $someEmpty ?? 'default string';
echo $foo; // "default string"

$zeroValue = 0;
$baz = $zeroValue ?? 42;
echo $baz; // 0
Enter fullscreen mode Exit fullscreen mode

PHP behaves exactly the same way here. The null coalescing operator follows the same rules and the same mental model as JavaScript, making it immediately familiar if you’re coming from a JS background.


2. Dynamic Import

JavaScript

Dynamic imports are especially useful in JavaScript applications because the code itself has to be downloaded. By loading modules only when they are actually needed, dynamic imports help keep the initial download small, improve startup time, and reduce the amount of JavaScript shipped to the browser.

await import('/modules/my-module.js');
Enter fullscreen mode Exit fullscreen mode

This makes features like code splitting and lazy loading first‑class citizens in modern frontend development.

PHP Equivalent (Conceptual)

PHP applications work very differently. The code runs on the server and does not need to be downloaded by the client, which makes dynamic imports far less critical from a performance perspective.

PHP loads files synchronously and doesn’t have a promise‑based model like JavaScript, but it still supports composing functionality at runtime:

require_once __DIR__ . '/modules/my-module.php';
Enter fullscreen mode Exit fullscreen mode

In modern PHP applications, this is usually handled by autoloading. With Composer’s autoloader, classes are loaded automatically when they are referenced without requiring an explicit import via require_once:

$service = new App\Service\MyService();
Enter fullscreen mode Exit fullscreen mode

While this isn’t a direct equivalent of JavaScript’s dynamic import(), PHP’s autoloading mechanism plays an important role in modern applications. It ensures that classes are loaded only when they are actually used, keeping the codebase modular and avoiding unnecessary includes, even though the underlying performance concerns differ from those in JavaScript.


3. Private Properties

JavaScript

Private properties prevents access to class internals from the outside.

class ClassWithPrivateField {
  #privateField = 42;

  getValue() {
    return this.#privateField;
  }
}
Enter fullscreen mode Exit fullscreen mode

Trying to access instance.#privateField will throw an error.

PHP Equivalent

PHP has supported private properties for a long time.

class ClassWithPrivateField {
    private int $privateField = 42;

    public function getValue(): int {
        return $this->privateField;
    }
}
Enter fullscreen mode Exit fullscreen mode

This gives you strong encapsulation that’s enforced at runtime. While JavaScript’s # syntax is relatively new, the idea itself will feel very familiar to PHP developers who have relied on private properties for years.


4. Logical Assignment Operators

JavaScript

Logical assignment operators assign values conditionally using logical operators.

const a = { duration: 50, title: '' };

a.duration ||= 10;
console.log(a.duration); // 50

a.title ||= 'title is empty.';
console.log(a.title); // "title is empty"
Enter fullscreen mode Exit fullscreen mode

In JavaScript, the ||= operator assigns a new value only if the left-hand side is falsy. That means values like false, 0, '', null, or undefined will all trigger the assignment. It’s a convenient shorthand when you want to guarantee a usable value, but it’s important to remember that it doesn’t distinguish between “missing” values and intentionally falsy ones.

PHP Equivalent

PHP doesn’t offer a direct ||= operator, but the underlying pattern is very familiar. A common way to express it is by reassigning the value using the ternary shortcut ?:.

$a = [
    'duration' => 50,
    'title' => ''
];

$a['duration'] = $a['duration'] ?: 10;
echo $a['duration']; // 50
Enter fullscreen mode Exit fullscreen mode

Since 50 is truthy, PHP keeps the original value.

$a['title'] = $a['title'] ?: 'title is empty.';
echo $a['title']; // "title is empty"
Enter fullscreen mode Exit fullscreen mode

Here, the empty string is considered falsy (becasue is a zero length string ''), so PHP falls back to the default.

It’s worth noting that PHP’s ?: behaves like JS’s ||=, it assigns a new value when the current one is falsy, including 0, false, '', or null.
If you want behavior closer to JS’s ??=, where only truly missing values trigger the assignment, the null coalescing operator ??= is the right choice.

Since PHP 7.4 (released in 2019), PHP has had the closest equivalent to JavaScript's ??=: the null coalescing assignment operator ??=. It assigns a value only if the variable is not set or is null, leaving all other values untouched.

$a['title'] ??= 'title is empty.';
Enter fullscreen mode Exit fullscreen mode

This mirrors JS's ??= behavior almost exactly and is usually the best choice when you want to provide a default only for missing values.


5. Iterator Methods

Modern JavaScript makes working with sequences of data super convenient thanks to helper methods like map, filter, and reduce, which can be applied to arrays — and, in some cases, even iterators.

JavaScript Example

const numbers = [1, 2, 3, 4, 5];

// Square the numbers, then keep only the even results, then sum them
const result = numbers
  .map(x => x * x)
  .filter(x => x % 2 === 0)
  .reduce((sum, x) => sum + x, 0);

console.log(result); // 20 -> (4 + 16)
Enter fullscreen mode Exit fullscreen mode

Here’s what happens step by step:

  1. .map squares each number → [1, 4, 9, 16, 25]
  2. .filter keeps only even numbers → [4, 16]
  3. .reduce sums them → 20

PHP Equivalent

While the fluent approach can improve readability, sometimes you can realize that the same logic can be implemented with a single loop, like this:

$result = 0;

foreach ($numbers as $number) {
  if ($number % 2 !== 0) {
    continue;
  }
$result += $number * $number;
}
Enter fullscreen mode Exit fullscreen mode

But if you want to find in PHP something similar to the previous JavaScript example, PHP has the functions for map, filter, and reduce an array:

$numbers = [1, 2, 3, 4, 5];

// Step 1: square the numbers
$squared = array_map(fn($x) => $x * $x, $numbers);

// Step 2: filter the even ones
$evenSquares = array_filter($squared, fn($x) => $x % 2 === 0);

// Step 3: sum them
$result = array_reduce($evenSquares, fn($sum, $x) => $sum + $x, 0);

echo $result; // 20
Enter fullscreen mode Exit fullscreen mode

PHP 8.5 introduced the pipe operator |>, so you can chain those functions:

$numbers = [1, 2, 3, 4, 5];

$result = $numbers
    |> (fn($arr) => array_map(fn($x) => $x * $x, $arr))
    |> (fn($arr) => array_filter($arr, fn($x) => $x % 2 === 0))
    |> (fn($arr) => array_reduce($arr, fn($sum, $x) => $sum + $x, 0));

echo $result; // 20
Enter fullscreen mode Exit fullscreen mode

Same JavaScript concept, slightly different syntax:

  • array_map = JS .map
  • array_filter = JS .filter
  • array_reduce = JS .reduce

The pipe operator in PHP 8.5 passes only one value forward. Since array_map, array_filter, and array_reduce take multiple arguments, they need a small wrapper function to fit into the pipeline (fn($arr) =>).

If you want a fluent approach like JS, you can evaluate using one of the open-source packages (just to say that in PHP, you have a lot of options).

PHP Equivalent with the Open-Source PHP Array Package

Using the PHP Array Package library, we can achieve a similar chainable, fluent syntax in PHP.

To install the package:

composer require hi-folks/array
Enter fullscreen mode Exit fullscreen mode

Once you have installed the package, you can start using it:

<?php

require "./vendor/autoload.php";

use HiFolks\DataType\Arr;


$result = Arr::make([1, 2, 3, 4, 5])
    ->map(fn($x) => $x * $x)
    ->filter(fn($x) => $x % 2 === 0)
    ->reduce(fn($sum, $x) => $sum + $x, 0);

echo $result; // 20 -> (4 + 16)
Enter fullscreen mode Exit fullscreen mode

The PHP Array Package is available here: https://github.com/Hi-Folks/array

This package makes PHP feel almost like JavaScript here, letting you map, filter, and reduce in a single, readable chain.


6. Hashbang Grammar

JavaScript

Hashbang grammar specifies the interpreter for executable scripts.

#!/usr/bin/env node

console.log("Hello world");
Enter fullscreen mode Exit fullscreen mode

This directove #!/usr/bin/env node allows JS files to be executed directly from the CLI.

To make the script executable:

chmod +x script.js
./script.js
Enter fullscreen mode Exit fullscreen mode

If you want to specify bun instead of node:

#!/usr/bin/env bun

console.log("Hello world");
Enter fullscreen mode Exit fullscreen mode

Hashbang grammar is technically a shell feature rather than a JavaScript-specific one. It allows a script to declare which executable should run it by specifying the interpreter (such as node) on the first line of the file.

PHP Equivalent

PHP is supported as well:

#!/usr/bin/env php
<?php

echo "Hello world";

Enter fullscreen mode Exit fullscreen mode

7. error.cause

JavaScript

It preserves the original error when rethrowing.

try {
  try {
    throw new Error("EXCEPTION 1");
  } catch (err) {
    throw new Error("EXCEPTION 2", { cause: err });
  }
} catch (err) {
  console.log(err.message); // EXCEPTION 2
  console.log(err.cause.message); // EXCEPTION 1
}
Enter fullscreen mode Exit fullscreen mode

This makes debugging and error tracing much easier.

PHP Equivalent

PHP supports exception chaining natively.

try {
    try {
        throw new Exception("EXCEPTION 1");
    } catch (Exception $err) {
        throw new Exception("EXCEPTION 2", 0, $err);
    }
} catch (Exception $err) {
    echo $err->getMessage(); // EXCEPTION 2
    echo $err->getPrevious()->getMessage(); // EXCEPTION 1
}
Enter fullscreen mode Exit fullscreen mode

You can later access the original exception:

$exception->getPrevious();
Enter fullscreen mode Exit fullscreen mode

This is one area where PHP has been ahead for quite some time.


Final Thoughts

This little exercise wasn’t about proving that one language is better than the other.

It was about:

  • Exploring the most loved JS features by JavaScript developers (thanks to the survey)
  • Appreciating how many of these ideas already exist in PHP
  • Seeing how languages keep learning from each other

Most of the time, the concepts are the same, only the syntax changes.

If you enjoyed this kind of cross-language comparison, feel free to share your thoughts or suggest another feature set to explore next. Happy coding! 🚀

Top comments (2)

Collapse
 
xwero profile image
david duymelinck • Edited

2. dynamic import

The main difference between JavaScript and PHP applications is that the JavaScript code needs to be downloaded, and that is where the dynamic imports help with keeping the download as small as possible.
Because PHP applications doesn't need to be downloaded the feature is less important.

5. Iterator Methods

While I like the readability of map, filter and reduce functions they have overhead. So when it is possible to do it in a single loop I prefer that.

$result = 0;

foreach($numbers as $number) {
   if($number % 2 != 0) {
      continue;
    }

    $result += $number * $number;
}
Enter fullscreen mode Exit fullscreen mode

This isn't that much harder to understand than chaining the functions.

In PHP 8.5 the chaining can be done with the pipe operator, no need for a builder pattern.

Collapse
 
robertobutti profile image
Roberto B.

Thanks @xwero for the feedback. Always appreciated! I’ve revised sections 2 and 5 accordingly. 🙌