Reading XLSX in PHP without a library

#   January 13, 2025,

I needed to read values from XLSX table. Existing PHP libraries seemed unnecessary heavy to me and I know that XLSX file basically just a ZIP archive so I thought that the data could be read directly from the XML files inside.

Here is the code. It reads data from the first sheet in the test.xlsx file and puts the (two dimensional) data into $rows variable.

$file = 'test.xlsx';

$zip = new ZipArchive();
if (!$zip->open($filePath) die('Failed to open file');

$sharedStringsXML = $zip->getFromName("xl/sharedStrings.xml");
$sheetXML = $zip->getFromName("xl/worksheets/sheet1.xml");

$sharedStrings = [];
if ($sharedStringsXML !== false) {
    $sharedStringsData = simplexml_load_string($sharedStringsXML);
    foreach ($sharedStringsData->si as $si) {
        $sharedStrings[] = (string)$si->t;
    }
}

$rowNumber = 0;
$rows = [];
$lastChar = false;
if ($sheetXML !== false) {
    $sheetData = simplexml_load_string($sheetXML)->sheetData;
    foreach ($sheetData->row as $row) {
        $rowNumber++;
        $rowData = [];
        foreach ($row->c as $cell) {
            $value = null;
            $r = (string)$cell['r'];
            $char = substr($r, 0, 1);
            if($lastChar) {
                $c1 = ord($char) - 65;
                $c2 = ord($lastChar) - 65;
                $diff = $c1 - $c2;
                if($diff > 1) {
                    for($i = 1; $i < $diff; $i++) {
                        $rowData[] = '';
                    }
                }
            }
            $lastChar = $char;
            $type = (string)$cell['t'];
            if ($type === 's') {
                $value = $sharedStrings[(int)$cell->v];
            } else {
                $value = (string)$cell->v;
            }
            $rowData[] = $value;
        }
        $rows[] = $rowData;
    }
}
$zip->close();

Doom in a PDF

#   January 13, 2025,

And I thought nothing would surprise me anymore...

Doom in a PDF

"It turns out that old versions of Emscripten can compile C to asm.js code that will happily run inside the limited JS runtime of the PDF engine.

I'm sorry, what? PDF has a JS runtime?

More on HN

Thin scrollbar, visible on hover

#   January 4, 2025, css

In some situations, the default browser scrollbars are too bulky and ugly. I did not know that there is an option to make them thinner, also visible only on hover:

.scrollbar {
    scrollbar-color: transparent transparent;
    scrollbar-width: thin;
}
.scrollbar.visible, .scrollbar:hover {
    scrollbar-color: #aaa transparent;
}

Thanks, Reddit!

PHP - sort strings with accents

#   December 10, 2024, php

Let's have an array with some strings:

$data = [
    'Abcd',
    'Efgh',
    'Červený trpaslík',
    'Another one',
    'Špenát',
    'Železo',
    'Zelenina'
];

I need to sort that. When using asort($data);, the strings with accents are moved to the end of the array:

Abcd
Another one
Efgh
Zelenina
Červený trpaslík
Špenát
Železo

To be able to sort correctly with the accents, let's use the PHP Collator-family functions:

usort($tags,
    function($a,$b) {
        $coll = collator_create( 'cs_CZ' );
        $aSortKey = collator_get_sort_key($coll, $a);
        $bSortKey = collator_get_sort_key($coll, $b);
        return $aSortKey >= $bSortKey;
    }
);

Now the data is sorted correctly:

Abcd
Another one
Červený trpaslík
Efgh
Špenát
Zelenina
Železo

Composer timeout

#   November 29, 2024, php,composer

When installing Laravel 10 using this command:

composer create-project laravel/laravel myproject

Composer started its job but timed out after ~5 minutes.

Fixed by adding two parameters:

composer --prefer-dist --no-dev create-project laravel/laravel myproject

Alternatively, it's possible to allow a longer time to run.

composer config --global process-timeout 2000

Laravel, Tailwind and Vite - a few notes

#   November 17, 2024, php,laravel,tailwind

The combination of Laravel, Tailwind and Vite has made my work a lot easier. Especially replacing the slow Webpack (Laravel Mix) with Vite was a great productivity boost, since the building times are much, MUCH, shorter.

I had to do just some small tweaks to make things work:

1. After installing Laravel, I had to install Tailwind using the official guide, this is pretty easy and straightforward.

2. Then I had to edit the package.json file and add the "watch" NPM script to be able to watch the CSS file changes and build the CSS on every change.

...
"scripts": {
"dev": "vite",
"build": "vite build",
"watch": "vite build --watch"
},
...

3. After adding the @vite(['resources/css/app.css', 'resources/js/app.js']) code to the HEAD section of my template, it still didn't work. When viewing the source code, I noticed that the <link> for the CSS still pointed at the local dev server with 5173 port.

After some searching, I found the solution here - I needed to delete the public/hot file.

After that, everything is working!

Multilingual website and browser language

#   December 25, 2023, php

The proposal is to create a multilingual website, using a main domain in .COM format accompanied by respective national domains for each language (.CZ, .PL, .DE, etc.).

The aim is for all national websites to redirect to the .COM domain, serving as the "primary" version in English. When a visitor lands on the .COM site with a browser language different from English, a popup should appear, offering the national website in their chosen language.

The first step involves redirecting all national websites to the main website. For instance, to redirect a .CZ website using mod_rewrite:

RewriteCond %{HTTP_HOST} ^website.cz$ [OR]
RewriteCond %{HTTP_HOST} ^www.website.cz$
RewriteRule (.*)$ https://www.website.com/$1 [R=301,L]

Then, detect a visitor language:

$visitorLanguage = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2); 
$allowedLanguages = ['en', 'de', 'fr', 'it', 'cs', 'sk'];
if(!in_array($visitorLanguage, $allowedLanguages)) $visitorLanguage = 'en';
define('VISITOR_LANGUAGE', $visitorLanguage);

CCompare it with the current website's locale (for instance, utilizing the $websiteLocale variable initialized from a cookie/URL fragment):

if($visitorLanguage != $websiteLocale) {
  // ... show a message to the visitor
  // offering a redirect to website with his language
}

PHP multibyte string functions are slow(er)

#   October 27, 2023, php

A reminder for myself: Only use mb_ functions in PHP when necessary, as they are significantly slower than their non-multibyte alternatives.

For example, replacing mb_substr with substr (where multibyte is not needed) resulted in 3x - 6x faster execution.

WSL CPU usage, killing the task

#   September 14, 2023,

From time to time, the wslservice.exe task spikes at around 80-90 % CPU usage. It can't be killed by closing the WSL terminal window.

I have to launch an elevated command prompt (or Powershell) and execute this command to kill the process:

taskkill /F /im wslservice.exe

Then I can launch it again without the CPU spike.