Originally published on php-to-js.learnio.dev — part of the PHP to JavaScript course. View this lesson on the original site.
Port a small PHP utility class to a JavaScript module
This project ties chapter 1 together: variables, strings, arrays, objects, functions, modules, and try/catch. You rewrite a PHP Formatter helper as exported functions in one .js file.
PHP habit: the starting class
<?php
namespace App\Utils;
class Formatter
{
public function formatCurrency(float $amount, string $currency = 'GBP'): string
{
return number_format($amount, 2) . ' ' . strtoupper($currency);
}
public function truncate(string $text, int $maxLength = 100, string $ellipsis = '…'): string
{
if (strlen($text) <= $maxLength) {
return $text;
}
return substr($text, 0, $maxLength) . $ellipsis;
}
public function slugify(string $text): string
{
$text = strtolower(trim($text));
$text = preg_replace('/[^a-z0-9\s-]/', '', $text);
$text = preg_replace('/[\s-]+/', '-', $text);
return trim($text, '-');
}
}
JavaScript equivalent: module shape
export function formatCurrency(amount, currency = 'GBP') {
return new Intl.NumberFormat('en-GB', {
style: 'currency',
currency,
}).format(amount);
}
export function truncate(text, maxLength = 100, ellipsis = '…') {
if (text.length <= maxLength) return text;
return text.slice(0, maxLength) + ellipsis;
}
export function slugify(text) {
return text
.toLowerCase()
.trim()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/[\s-]+/g, '-')
.replace(/^-+|-+$/g, '');
}
| PHP | JavaScript | Why |
|---|---|---|
class Formatter |
Named export function |
One responsibility per function |
number_format() |
Intl.NumberFormat |
Locale-aware currency |
substr($s, 0, $n) |
text.slice(0, n) |
Same slicing idea |
preg_replace(...) |
text.replace(/.../g, ...) |
Regex on strings |
Your task
- Implement the three functions in the sandbox (or in your editor with a
formatter.jsfile). - Build a small catalog array of products with
titleandprice. - For each product, log: truncated title, formatted price, and slug URL path.
- Wrap
formatCurrencyinput intry/catchif you add validation that throws on bad data.
Example output shape:
Running shoes — Trail Ed… £89.99 /products/running-shoes-trail-edition
Merino Wool Socks £12.50 /products/merino-wool-socks
GitHub example
The course repo includes formatter.php, a sample formatter.js, and main.js under the chapter 1 example folder. Compare your version after you finish — not before.
Try it
Use the amber Try it panel below as a starter. Extend it with your own product list.
function formatCurrency(amount, currency = 'GBP') {
return new Intl.NumberFormat('en-GB', { style: 'currency', currency }).format(amount);
}
function truncate(text, maxLength = 30, ellipsis = '…') {
if (text.length <= maxLength) return text;
return text.slice(0, maxLength) + ellipsis;
}
function slugify(text) {
return text
.toLowerCase()
.trim()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/[\s-]+/g, '-');
}
const products = [
{ title: 'Running shoes — Trail Edition', price: 89.99 },
{ title: 'Merino Wool Socks', price: 12.5 },
];
for (const { title, price } of products) {
const label = truncate(title);
const amount = formatCurrency(price);
const slug = slugify(title);
console.log(`${label.padEnd(28)} ${amount.padStart(8)} /products/${slug}`);
}
When this runs cleanly, you have a working end-to-end port — the chapter 1 outcome.
From PHP to JavaScript — JavaScript Syntax Foundations for PHP Devs