What Claude Code Can Do

The other day I told Claude: “Turn off wifi.” And it did – because it knows how to do that from the command line. Then I said: “Turn on wifi.” And it did nothing. Because it had no internet.

This experience nicely illustrates what Claude Code actually is. It's not just a chatbot you talk to. It's a tool that has access to your computer and can perform actions on it. It can read and edit files, run programs, install software, work with data. Basically anything you'd normally do manually.

In the previous article we showed how to install Claude Code. Now let's look at what you can use it for. And trust me – there's a lot more than you'd expect.

Image

Programming

Let's start with the most expected use case. Claude Code is primarily a tool for programmers, and it truly excels in this area.

Writing new code. Describe what function you need, and Claude will write it. You don't need to know the syntax, you don't need to google – just say what the code should do, and it creates it. A team of data engineers at Anthropic had Claude write a complete React application – 5,000 lines of TypeScript – even though they didn't know React themselves.

Bug fixes. Throw an error message at it and let it find the cause and fix it. Claude will go through your code, understand the context, and suggest a fix. You don't have to spend hours figuring out where the problem is.

Refactoring. Got old messy code? Claude will clean it up, split long functions into smaller ones, rename variables to meaningful names. One developer described how he refactored a 210-line Python function down to less than 30 lines.

Tests. Claude will write unit tests for your code. It can even work with test-driven development – first writing tests, then code that passes them.

Documentation. It documents functions, creates README files, describes APIs. Everything programmers hate doing manually.

Code review. It goes through code and suggests improvements – better names, more efficient algorithms, potential bugs.

Working with files

This is where Claude Code becomes interesting even for non-programmers. It has access to your files and can work with them.

Organization. Got a chaotic Downloads folder full of randomly named files? Claude will sort it out. It'll create subfolders by type, date, project – whatever you need.

Bulk renaming. Need to rename hundreds of files according to a certain pattern? For example, renaming invoices to “2024–01–15 Vendor – Invoice.pdf”? Claude handles it in seconds.

Format conversion. Converts data between formats – CSV to JSON, XML to a table, whatever you need.

Writing and working with text

Although it's called “Code,” Claude Code is surprisingly good at helping with text.

Articles and blogs. Helps structure thoughts, write first drafts, edit and improve. One user called it the best writing support system they'd ever tried.

Organizing notes. Got chaotic notes scattered across different files? Claude will go through them and organize them into a coherent whole. One user uploaded voice notes from walks with a stroller – Claude transcribed them, organized them into research topics, and eventually wrote an article in her style.

Translations. Translates text while preserving formatting. Can even batch translate all text files in a project.

Proofreading. Fixes spelling, grammar, style. Suggests better phrasing.

Data analysis

Unlike web Claude, which has file size limits, Claude Code can process huge datasets. And most importantly – it can write its own code for analysis and run it immediately.

Reports on demand. You tell it: “Look at this sales data and create a report with charts and trend analysis.” And it does it. Loads the data, analyzes it, creates visualizations, and writes a summary.

Finding patterns. Claude finds anomalies and trends that you'd spend hours looking for manually. Which product has declining sales? Where are the seasonal fluctuations?

Data cleaning. Got a list of addresses full of typos and duplicates? Claude will clean it up, standardize the format, and remove errors.

One user described how they simply uploaded a CSV file with customer data and said: “Imagine you're a data consultant. Perform an analysis and suggest recommendations.” Claude went through the data and delivered a detailed report with findings.

Automation

This is where Claude Code truly shines. It can create automated processes that save you hours of work.

Repetitive tasks. Got something you do the same way every day or week? Claude will automate it. Downloading data, generating reports, sending notifications.

Connecting services. Claude can connect different tools together. Anthropic's marketing team used this approach to create a system that automatically generates hundreds of ad text variations from a CSV file with ad performance data.

Monitoring and notifications. You can have Claude monitor logs or data and alert you when something breaks or an interesting pattern appears.

System administration

Claude Code can function as your personal IT support.

Diagnostics. “Why is my computer slow?” – and Claude checks CPU usage, memory, disk, running processes, Docker containers, everything possible. And suggests solutions.

Configuration. Need to set something up in your system but don't know how? Describe what you want, and Claude will configure it.

Installation. Claude installs software, configures the environment, sets up dependencies.

One user described how Claude connected via SSH to a remote server, modified the configuration of a monitoring tool, and adapted when some commands failed.

For more advanced users, Claude Code also helps with infrastructure management. It sets up automatic builds and deploys, configures server monitoring, helps with Docker containers. Even a non-technical person can set up a basic CI/CD pipeline with Claude's help.

But beware – it's still AI

Before you get carried away with enthusiasm, there's one important thing to say: Claude Code makes mistakes. Like any AI. Sometimes small ones, sometimes… well, sometimes monumental ones.

One programmer told me the following story. He gave Claude the task of modernizing an outdated project. Claude ran for an hour. Rewrote everything. Legacy code from 2015? Gone. Outdated patterns? Replaced with elegant modern solutions. Confusing architecture? Now crystal clear. The result looked like it was from a textbook – TypeScript, tests, documentation, everything.

It was absolutely top-notch code.

Which didn't work at all.

That's why this holds true: the better programmer you are and the more you understand your field, the better results you'll achieve with Claude Code. Not because you'd have to write the code yourself – but because you'll recognize when Claude makes a mistake. And it will. The question isn't if, but when.

Think of Claude as a junior with unlimited energy and encyclopedic knowledge who still needs supervision. Let it work, but check the results. Test. Verify it actually works before you push it to production.

What does this mean?

Claude Code isn't just a tool for programmers. It's an “everything agent” – a universal assistant that has access to your computer and can perform almost any task.

The key to success is simple: describe what you want in plain language. You don't need to know commands, you don't need to understand technicalities. Just say what you want to achieve, and Claude will find a way to do it.

Users report 40–80% time savings on routine tasks. Some startups have built entire products with practically no traditional programming. All because you have an intelligent helper at your disposal who can control your computer.

Just watch out for that wifi.

The Ultimate PHP Comparison Reference is Here!

No more writing test scripts when you're not entirely sure. No more tedious documentation diving. The PHP truth table has finally arrived. I've prepared the definitive PHP Comparison Cheat Sheet for you. It's your map for the territory where === doesn't rule.

Since PHP 8 rewrote the comparison rules, there are two tables:

👉 Table for PHP 8.x (The present you need to master)
👉 Table for PHP 7.x (For legacy warriors and code archaeologists)

Image

We've all learned to use === for a good night's sleep. It's our safety net. But what happens when you don't need to know if values are identical, but which one is greater or smaller? That's where certainty evaporates. The operators <, >, <=, and >= have no “strict” version. PHP grabs the wheel and fires up type juggling. Do you know for certain how comparing a number to a string behaves? Or null to false?

Just check the table and instantly see how types interact when PHP forces them together. It starts with a comprehensive overview of all operators, including the spaceship (<=>).

Catch Silent Bugs Before They Strike

Here are two examples that can cost you hours of debugging. Different PHP functions use different comparison strategies. The sort() function, for instance, defaults to SORT_REGULAR.

How does it handle strings that look like numbers, such as "042" and " 42"? How will it sort them?

  • I find the SORT_REGULAR section in the table
  • I look up the intersection of these values
  • I see the symbol =
  • What it means: PHP treats them as identical in this mode. The resulting order of these elements after sorting will be undefined. If order matters, we've got a problem.

What about array_unique()? Will it silently “cannibalize” my data when "042" and " 42" meet in the array? No need to test.

  • The array_unique() function defaults to SORT_STRING
  • I check the intersection of these values
  • I see the symbol <
  • What it means: We're in the clear – the values differ (because everything converts to string)

Thanks to the table, no more guessing. You see instantly when you need to switch flags to make your application behave exactly as intended.

(And yes, there's no flag for strict comparison without type juggling in PHP 😤)

The Fun Stuff: DateTime and Closures

Or what you probably don't know about comparing objects in PHP.

Take DateTime. Many developers believe objects can't be compared, so they desperately convert dates to timestamps or formatted strings like 'Y-m-d H:i:s' just to figure out what came first. Completely unnecessary! The DateTime and DateTimeImmutable classes have built-in logic for standard comparison operators. You can check for greater/smaller as naturally as with numbers. No helpers, no formatting, just clean syntax. That's why it deserves its own DateTime section in the table.

The real fun starts with equality. While === is ruthless with objects and only cares if you're holding the exact same instance, the == operator takes a more pragmatic approach with dates – it compares the time value. This means you can compare two different objects, and if they represent the same moment in time, PHP says “yes, these are equal”. Better yet – it works cross-class between DateTime and DateTimeImmutable!

And the cherry on top? Closures. Even anonymous functions are objects. When are two closures equal? Check the table!

PHP 8.5’s Pipe Operator is Lipstick on a Pig

Finally! We are popping the Dom Pérignon, tearing up the confetti, and ritually burning our procedural programming textbooks. PHP 8.5 brings the legendary Pipe Operator |>. The holy grail for everyone who prays to the functional programming gods at night and secretly envies the hipsters in Elixir or F#.

No more nesting functions inside each other like a Russian Matryoshka doll. No more helper variables like $tmp1, $tmp2, $tmp47. We are writing data flow from left to right, exactly the way we naturally think!

Image

The PHP marketing department would put this on their slides:

$result = " Hello " |> trim(...) |> strtoupper(...);

“Wow! Such purity! Such elegance!” the crowd screams, throwing their bras onto the stage.

But then you wake up and realize reality is a different beast entirely. Welcome to the hell of parentheses, anonymous functions, and performance masochism. Let's look at why this new feature is about as useful as a waterproof tea bag.

Case Study: How to write the same thing, but more complicated

Imagine a classic scenario: You want to normalize text for English subtitles. The process: trim whitespace, split into words, capitalize the first letter of each word, and glue it back together.

Compare for yourself:

// Traditional nesting - unreadable horror (Matryoshka style)
$result = implode(' ', array_map(ucfirst(...), preg_split('/\s+/', trim($input))));

// With the Pipe Operator - WOW! 🎉
$result = $input
    |> trim(...)
    |> preg_split('/\s+/', ...)
    |> array_map(ucfirst(...), ...)
    |> implode(' ', ...);

At first glance, it’s a Zen garden. You see data flowing from top to bottom like a waterfall. Your brain purrs with bliss because it doesn't have to decipher parentheses from the inside out. It's like reading a recipe: “Take input, trim it, split into words, adjust, glue.” Beautiful. Feng-shui in practice.

EXCEPT THAT’S NOT HOW IT WORKS!

That beautiful example above is a dirty syntactic lie. The PHP parser would choke and die screaming SYNTAX ERROR if it tried to process that code.

Let's explain why right now.

How it was done in the past

We’ve already seen the unreadable nesting horror. Instead, let's look at the “peasant” style we all secretly use when no one is looking:

// Traditional way with helper variables - clear, functional, boring (110 chars)
$_ = trim($input);                             // remove whitespace
$_ = preg_split('/\s+/', $_);                  // split into words
$_ = array_map(ucfirst(...), $_);              // capitalize letters
$processed = implode(' ', $_);                 // join back

Clear, readable. Every junior dev knows what's happening. The helper variable $_ won't win a beauty contest, but it works, costs nothing, and doesn't get in the way.

By the way, this code has 110 characters. Remember that number.

First Slap: ... is not Partial Application

The ... placeholder ONLY works when you are piping into the first and simultaneously the only parameter!

So while trim(...) is fine, anything more complex hits a brick wall. PHP (unlike languages that do it properly) can't say “here is a function and here is the hole for the argument.”

And because most functions in PHP have a parameter order chosen by a random number generator (the needle/haystack chaos), you have to use arrow functions. Get your fingers ready, you’ll be typing a lot of fn, $_, and arrows:

$processed = $input
    |> trim(...)                               // The only moment it works nicely
    |> fn($_) => preg_split('/\s+/', $_)       // Arrow func. Variable.
    |> fn($_) => array_map(ucfirst(...), $_)   // Nested arrow func, yum.
    |> fn($_) => implode(' ', $_);

Second Slap: Parenthesis Hell

You run it, expecting applause. Instead, the PHP interpreter spits out:
Fatal error: Arrow functions on the right hand side of |> must be parenthesized.

Excuse me? I have to put arrow functions in parentheses? Why? Because of operator precedence. The parser is as confused as a headless chicken, so you have to explicitly wrap every arrow function so it understands where one pipe ends and the next begins.

So your “elegant” code now looks like this:

$processed = $input
    |> trim(...)
    |> (fn($_) => preg_split('/\s+/', $_))     // Parenthesis. Arrow func. Variable. Parenthesis.
    |> (fn($_) => array_map(ucfirst(...), $_)) // More parentheses...
    |> (fn($_) => implode(' ', $_));           // Why are we doing this to ourselves?

Congratulations! Your code is now:

  • 53% longer than the helper variable variant (now has 169 characters).
  • Visually resembles a lobotomized LISP (nothing but parentheses).
  • Carries the overhead of creating a closure at every single step.

You can't stop progress!

K.O.: References? Forget about it

This is the part where laughter turns into tears and gnashing of teeth. Imagine you want to replace something in the text and you're interested in how many times the replacement happened (the &$count parameter in str_replace).

In the classic “outdated” code, you would simply pass the variable by reference.

$_ = str_replace('!', '', $_, count: $count); // $count happily increments

But inside arrow functions within the pipe? Tough luck. Arrow functions (fn) in PHP capture variables from the outer scope by value (copy).

Look at this bear trap:

$count = 0;

// We expect $count to increase...
$processed = $input
    |> (fn($_) => str_replace('!', '', $_, count: $count)) // Betrayal!
    |> trim(...)
    ...

echo $count; // The result is always 0. Zero. Zilch.

What happened?

Nothing. No error. No Notice. No Warning. PHP simply silently took a copy of zero, sent it into the function, the function happily incremented it inside its own little bubble, and then threw it in the trash. Your original variable $count remained untouched.

If you want to fix this, you have to use the old syntax function($_) use (&$count) { return ... }, thereby losing the last shreds of dignity and elegance.

What happens under the hood? (Spoiler: Nothing pretty)

You might be thinking: “Okay, it's ugly, but surely it's a super-fast optimized macro, right?”

(No sane person thinks this, but let's pretend.)

The Pipe Operator is not a smart macro that rewrites (fn($_) => ...) code into simple calls. Every single step really creates a new instance of a Closure object. For every operation. For every line.

So instead of a simple function call, PHP internally does this:

  1. Create a Closure object.
  2. Call it.
  3. Throw it away (and let the Garbage Collector have a seizure).
  4. Repeat for the next line.

It is like ordering an Uber to go to the fridge for a beer. You get there, but it is unnecessarily expensive, takes longer, and the neighbors will think things about you.

To Typehint, or Not to Typehint?

This is the Hamlet-esque question of modern PHP. In a chain of pipe operators, you have to decide:

Option A: I am an honest masochist and I write (fn(string $_): array => ...).

Result: The code is so long it doesn't fit on two monitors side-by-side. You will wear your hands down to stumps and your colleagues will hate you during every Code Review.

Option B: I am a lazy punk and I just write (fn($_) => ...).

Result: Your IDE stops hinting and strictly configured static analysis (PHPStan) starts screaming “Mixed type everywhere!”

It is a choice between carpal tunnel syndrome and coding blind.

Verdict

The PHP 8.5 Pipe Operator is like that expensive designer juicer you bought in January during a fit of healthy lifestyle enthusiasm. It looks nice on the counter, you tell everyone about it, but when you actually have to use it, you realize that cleaning the mesh sieve takes three times longer than just eating the whole orange, peel and all.

The Pipe Operator only makes sense if:

  1. All functions take exactly one parameter. ✅
  2. Or you don't care about performance. ✅
  3. Or you really love parentheses. ✅

So… it's great for tutorials and conferences!

Recommendation: Stick to helper variables. They are cheap, they work, they don't block references, and you don't have to write (fn($_) => ...) ten times in a row.


P.S.: PHP 8.6 will solve this with Partial Function Application. But taking comfort in that now is like telling a hungry man: ‘Hang in there, next year the schnitzel will actually have meat.’

How to Install Claude Code: A Beginner's Guide

(Updated January 2026) Claude Code is a tool that brings AI capabilities in the form of a virtual programmer. Think of it as a colleague sitting beside you, helping with your projects. Unlike regular browser-based chatting where you copy code back and forth, Claude Code works directly with your files on your computer.

What You Can Do With It

Here are just a few examples of what you can accomplish:

  • 💡 Describe the function you need, and Claude writes it for you
  • 🐛 Pass it an error message and let it hunt down and fix the problem
  • 🔧 Refactor existing code, add tests, write documentation
  • 🗺️ When you need to understand an unfamiliar project, just ask how it all works

And here's what you might not know – Claude Code isn't just for programming. Since it has access to your terminal, you can ask it to handle all sorts of tasks:

  • 📋 Organize your downloads folder
  • ⚙️ Install packages and configure your system
  • 📁 Batch rename hundreds of files or search through data

Basically anything you'd normally do on the command line – just instead of googling commands, you describe what you want in plain English and Claude does it for you. We'll explore more in the article What Claude Code Can Do.

Who This Guide Is For

You don't need to be an AI expert or a seasoned programmer – Claude Code is designed to be accessible to everyone. That said, the better you understand programming and your domain, the better results you'll get.

This first article is aimed at complete beginners and walks you through installing Claude Code. In later parts, we'll dive into practical usage, but first we need to tackle that crucial first step – getting it up and running. By the end of this guide, you'll have everything set up and ready to start exploring Claude Code.

…pokračování

100 minutes is less than 50? PHP paradoxes during time changes

“When shall we meet?” – “Tomorrow at three.” “When is that meeting?” – “Next month.” For everyday life, such time specifications are perfectly adequate. But try the same in programming and you'll quickly discover you've entered a labyrinth full of traps and unexpected surprises.

Time in programming is like a beast that appears tame until you step on its tail. And one of this beast's most powerful tricks is daylight saving time with its insidious transitions. A system supposedly created to save candles now causes programmers sleepless nights (probably around 2:30 AM when they suddenly realize their servers are doing weird things).

Let's explore the dark corners of daylight saving time transitions, how PHP (mis)handles them, and how I attempted to fix this madness in Nette Utils. Prepare for moments when 1 + 1 ≠ 2 and when adding a longer time paradoxically returns an earlier hour. Not even Einstein could have conceived of this.

Image

First, let's run through some terminology

Before diving into the problem, let's explain a few key concepts:

  • UTC (Coordinated Universal Time) – the fundamental time standard from which all other time zones are derived. It's essentially the “zero point” for measuring time across the world.
  • Time offset – how many hours need to be added to or subtracted from UTC to obtain local time. It's denoted as UTC+X or UTC-X.
  • CET (Central European Time) – the Central European Time we use in winter. It has an offset of UTC+1, which means that when it's noon in UTC, it's 1:00 PM here.
  • CEST (Central European Summer Time) – the Central European Summer Time we use in summer. It has an offset of UTC+2, so when it's noon in UTC, it's 2:00 PM here.
  • Daylight Saving Time – a system where during a certain part of the year (usually summer) we move the clocks forward by one hour to better utilize daylight.

That moment lasted a whole light-year

Let's break down, second by second, how the transition to daylight saving time and back works. As an example, let's take the recent time change in the Czech Republic on Sunday, March 30, 2025:

…pokračování

Var, Let, Const: Stop Complicating Your Life in JavaScript

JavaScript offers three ways to declare variables: var, let, and const. Many programmers aren't entirely clear on when to use which one, and most tutorials and linters force you to use them incorrectly. Let's see how to write cleaner and more understandable code without unnecessary rules that don't actually help us.

Let's Start with the Most Dangerous Part

JavaScript has one treacherous quirk: by simply omitting a variable declaration, you can unknowingly use a global variable. All it takes is forgetting var, let, or const:

function calculatePrice(amount) {
    price = amount * 100;    // Omission! Missing 'let'
    return price;            // We're using a global variable 'price'
}

function processOrder() {
    price = 0;               // We're using the same global variable!
    // ... some code calling calculatePrice()
    return price;            // We're returning a completely different value than expected
}

This is every developer's nightmare – the code appears to work correctly until something mysteriously starts failing elsewhere in the application. Debugging such errors can take hours because a global variable can be overwritten anywhere in the application.

That's why it's absolutely crucial to always declare variables using let or const.

Forget About var

The var keyword has been in JavaScript since its inception in 1995 and carries some problematic properties that were considered features at the time of the language's creation but proved to be a source of many bugs over time. After twenty years of language development, JavaScript's authors decided to address these problems – not by fixing var (to maintain backward compatibility) but by introducing the new let keyword in ES2015.

You can find plenty of articles on the internet dissecting the problems with var in the finest detail. But you know what? There's no need to get bogged down in the details. Let's just treat var as a relic of the past and focus on modern JavaScript.

When to Use let

let is the modern way to declare variables in JavaScript.

The nice thing is that the variable only exists within the code block (between curly braces) where it was defined. This makes the code more predictable and safer.

if (someCondition) {
    let temp = calculateSomething();
    // temp is only available here
}
// temp no longer exists here

In loops, the declaration is technically placed before the curly braces, but don't let that confuse you – the variable only exists within the loop:

for (let counter = 0; counter < 10; counter++) {
    // The counter variable only exists in the loop
}
// counter is no longer accessible here

When to Use const

const is used to declare constants. These are typically important values at the module or application level that should never change:

const PI = 3.14159;
const API_URL = 'https://api.example.com';
const MAX_RETRY_ATTEMPTS = 3;

However, it's important to understand one key detail: const only prevents assigning a new value to the variable – it doesn't control what happens with the value itself. This distinction is particularly evident with objects and arrays (an array is also an object) – const doesn't make them immutable objects, i.e., it doesn't prevent changes inside the object:

const CONFIG = {
    url: 'https://api.example.com',
    timeout: 5000
};

CONFIG.url = 'https://api2.example.com';  // This works!
CONFIG = { url: 'https://api2.example.com' };  // This throws TypeError!

If you need a truly immutable object, you need to freeze it first.

The let vs const Dilemma

Now we come to a more interesting question. While the situation with var vs let is clear, the use of const is the subject of many community discussions. Most tutorials, style guides, and linters promote the rule “use const wherever you can.” So we commonly see const used in function or method bodies.

Let's explain why this popular “best practice” is actually an anti-pattern that makes code less readable and unnecessarily restrictive.

The approach “if a variable's value isn't reassigned in the code, it should be declared as const” seems logical at first glance. Why else would const even exist? The more “constants,” the safer and more predictable the code, right? And faster too, because the compiler can better optimize it.

However, this entire approach fundamentally misunderstands the purpose of constants. It's primarily about communicating intent – are we truly trying to signal to other developers that this variable should never be reassigned, or do we just happen not to reassign it in our current implementation?

// Real constants - values that are constant by their nature
const PI = 3.14159;
const DAYS_IN_WEEK = 7;
const API_ENDPOINT = 'https://api.example.com';

// vs.

function processOrder(items) {
    // These AREN'T constants, we just happen to not reassign them
    const total = items.reduce((sum, item) => sum + item.price, 0);
    const tax = total * 0.21;
    const shipping = calculateShipping(total);
    return { total, tax, shipping };
}

In the first case, we have values that are constants by their nature – they express immutable properties of our system or important configuration data. When we see PI or API_ENDPOINT somewhere in the code, we immediately understand why these values are constants.

In the second case, we're using const just because we happen to not reassign the values right now. But that's not their essential characteristic – these are regular variables that we might want to change in the next version of the function. And when we want to do that, const will unnecessarily prevent us.

In the days when JavaScript was one big global code, it made sense to try to secure variables against reassignment. But today we write code in modules and classes. Today it's common and correct that the scope is a small function, and within its scope, it makes no sense to worry about the difference between let and const.

Because it creates completely unnecessary mental overhead:

  1. The programmer has to think while writing: “Will I change this value? No? Then I must use const…”
  2. It distracts readers! When they see const in the code, they wonder: “Why is this a constant? Is this some important value? Does it have any significance?”
  3. In a month we need to change the value and have to deal with: “Can I change const to let? Is someone relying on this?”

Simply use let and you don't have to deal with these questions at all.

It's even worse when this decision is made automatically by a linter. That is, when the linter “fixes” variables to const because it only sees one assignment. The code reader then unnecessarily wonders: “Why must these variables be constants here? Is it somehow important?” And yet it's not important – it's just a coincidence. Don't use the prefer-const rule in ESLint!

By the way, the optimization argument is a myth. Modern JavaScript engines (like V8) can easily detect whether a variable is reassigned or not, regardless of whether it was declared using let or const. So using const provides no performance benefit.

Implicit Constants

In JavaScript, there are several constructs that implicitly create constants without us having to use the const keyword:

// imported modules
import { React } from 'react';
React = something; // TypeError: Assignment to constant variable

// functions
function add(a, b) { return a + b; }
add = something; // TypeError: Assignment to constant variable

// classes
class User {}
User = something; // TypeError: Assignment to constant variable

This makes sense – these constructs define the basic building blocks of our code, and overwriting them could cause chaos in the application. That's why JavaScript automatically protects them against reassignment, just as if they were declared using const.

Constants in Classes

Classes were added to JavaScript relatively recently (in ES2015), and their functionality is still gradually maturing. For example, private members marked with # didn't arrive until 2022. JavaScript is still waiting for class constant support. For now, you can use static, but it's far from the same thing – it marks a value shared between all class instances, not an immutable one.

Conclusion

  1. Don't use var – it's outdated
  2. Use const for real constants at the module level
  3. In functions and methods, use let – it's more readable and clearer
  4. Don't let the linter automatically change let to const – it's not about the number of assignments, but about intent

How to Deal with the Chaos of Empty Strings and NULL Values in MySQL?

You know the situation – you create a query WHERE street = '', but the system doesn't return all the records you'd expect. Or your LEFT JOIN doesn't work as it should. The reason is a common problem in databases: inconsistent use of empty strings and NULL values. Let's see how to solve this chaos once and for all.

When to Use NULL and When to Use an Empty String?

In theory, the difference is clear: NULL means “value is not set”, while an empty string means “value is set and is empty”. Let's look at a real example from an e-commerce site, where we have an orders table. Each order has a required delivery address and an optional billing address for cases where the customer wants to bill to a different location (typical checkbox “Bill to a different address”):

CREATE TABLE orders (
    id INT PRIMARY KEY,
    delivery_street VARCHAR(255) NOT NULL,
    delivery_city VARCHAR(255) NOT NULL,
    billing_street VARCHAR(255) NULL,
    billing_city VARCHAR(255) NULL
);

The billing_city and billing_street fields are nullable because the billing address is optional. But there's a difference between them. While a street can be legitimately empty (villages without street names) or unset (delivery address is used), the city must always be filled in if a billing address is used. So either billing_city contains a city name, or it's NULL – in which case the delivery address is used.

The Reality of Large Databases

In practice, both approaches often end up being mixed in the database. There can be several reasons:

  • Changes in application logic over time (e.g., switching from one ORM to another)
  • Different teams or programmers using different conventions
  • Buggy data migrations when merging databases
  • Legacy code that behaves differently than new code
  • Application bugs that occasionally let through an empty string instead of NULL or vice versa

This leads to situations where we have a mix of values in the database and need to write complex conditions:

SELECT * FROM tbl
WHERE foo = '' OR foo IS NULL;

Even worse is that NULL behaves unintuitive when comparing:

SELECT * FROM tbl WHERE foo = ''; -- doesn't include NULL
SELECT * FROM tbl WHERE foo <> ''; -- also doesn't include NULL

-- we must use
SELECT * FROM tbl WHERE foo IS NULL;
SELECT * FROM tbl WHERE foo <=> NULL;

This inconsistency in comparison operators' behavior is another reason why it's better to use only one way of representing empty values in the database.

Why Avoid the Dual Approach

A similar situation exists in JavaScript, where we have null and undefined. After years of experience, many JavaScript developers concluded that distinguishing between these two states brings more problems than benefits and decided to use only the system-native undefined.

In the database world, the situation is similar. Instead of constantly dealing with whether something is an empty string or NULL, it's often simpler to choose one approach and stick to it. For example, Oracle database essentially equates empty strings and NULL values, thus elegantly avoiding this problem. It's one of the places where Oracle deviates from the SQL standard, but it simplifies working with empty/NULL values.

How can we achieve something similar in MySQL?

What Do We Actually Want to Enforce?

  1. For required fields (NOT NULL), we want to enforce that they always contain meaningful values. That means preventing empty strings (or strings containing only spaces)
  2. For optional fields (NULL), we want to prevent storing empty strings. When a field is optional, NULL should be the only representation of an “unfilled value”. Mixing both approaches in one column leads to problems with querying and JOIN operations, as we showed above.

Solution in MySQL

Historically in MySQL, it made sense to use exclusively empty strings ('') instead of NULL values. It was the only approach that could be enforced using the NOT NULL constraint. If we wanted an automatically consistent database, this was the only way.

However, there's one important case where this approach fails – when we need a unique index on the column. MySQL considers multiple empty strings as the same value, while multiple NULL values are considered different.

However, since MySQL version 8.0.16, we can use CHECK constraints and have more control over what values we allow. We can, for example, enforce that a column will either be NULL or contain a non-empty string:

CREATE TABLE users (
    id INT PRIMARY KEY,

    -- Required field - must contain some non-empty text
    email VARCHAR(255) NOT NULL UNIQUE
        CONSTRAINT email_not_empty      -- rule name
        CHECK (email != ''),

    -- Optional field - either NULL or non-empty text
    nickname VARCHAR(255)
        CONSTRAINT nickname_not_empty
        CHECK (nickname IS NULL OR nickname != '')
);

When creating a CHECK constraint, it's important to give it a meaningful name using the CONSTRAINT keyword. This way, we get a meaningful error message Check constraint ‘nickname_not_empty’ is violated instead of a generic constraint violation notice. This significantly helps with debugging and application maintenance.

The problem isn't just empty strings, but also strings containing only spaces. We can improve the CHECK constraint solution using the TRIM function:

CREATE TABLE users (
    id INT PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE
        CONSTRAINT email_not_empty
        CHECK (TRIM(email) != ''),
   ...
);

Now these validation bypass attempts won't work either:

INSERT INTO users (email) VALUES ('   ');  -- all spaces

Practical Solution in Nette Framework

A consistent approach to empty values needs to be handled at the application level too. If you're using Nette Framework, you can use an elegant solution using the setNullable() method:

$form = new Form;
$form->addText('billing_street')
    ->setNullable(); // empty input transforms to NULL

Recommendations for Practice

  1. At the start of the project, decide on one approach:
    • Either use only NULL for missing values
    • Or use only empty strings for empty/missing values
  2. Document this decision in the project documentation
  3. Use CHECK constraints to enforce consistency
  4. For existing projects:
    • Conduct an audit of the current state
    • Prepare a migration script to unify the approach
    • Don't forget to adjust application logic

With this approach, you'll avoid many problems with comparing, indexing, and JOIN operations that arise from mixing NULL and empty strings. Your database will be more consistent and queries simpler.

Renaming ENUM Values Without Data Loss: A Safe Guide

Renaming values in a MySQL ENUM column can be tricky. Many developers attempt a direct change, which often results in data loss or errors. We'll show you the correct and safe way to do it.

Imagine a typical scenario: You have an orders table in your database with a status column of type ENUM. It contains the values waiting_payment, processing, shipped, and cancelled. The requirement is to rename waiting_payment to unpaid and shipped to completed. How can this be done without risk?

What Doesn't Work

First, let's look at what does not work. Many developers try this straightforward approach:

-- THIS DOES NOT WORK!
ALTER TABLE orders
MODIFY COLUMN status ENUM(
    'unpaid',      -- previously 'waiting_payment'
    'processing',  -- unchanged
    'completed',   -- previously 'shipped'
    'cancelled'    -- unchanged
);

This approach is a recipe for disaster. MySQL will attempt to map existing values to the new ENUM, and since the original values are no longer in the definition, it will either replace them with an empty string or return the error Data truncated for column 'status' at row X. In a production database, this would mean losing important data.

Backup First!

Before making any structural changes to your database, it is absolutely crucial to create a data backup. Use MySQL-dump or another trusted tool.

The Correct Approach

The correct approach consists of three steps:

  1. First, extend the ENUM with new values.
  2. Update the data.
  3. Finally, remove the old values.

Let's go through it step by step:

1. The first step is to add the new values to the ENUM while keeping the original ones:

ALTER TABLE orders
MODIFY COLUMN status ENUM(
    'waiting_payment',  -- original value
    'processing',       -- unchanged
    'shipped',         -- original value
    'cancelled',       -- unchanged
    'unpaid',          -- new value (replaces waiting_payment)
    'completed'        -- new value (replaces shipped)
);

2. Now we can safely update the existing data:

UPDATE orders SET status = 'unpaid' WHERE status = 'waiting_payment';
UPDATE orders SET status = 'completed' WHERE status = 'shipped';

3. Finally, once all data has been converted to the new values, we can remove the old ones:

ALTER TABLE orders
MODIFY COLUMN status ENUM(
    'unpaid',
    'processing',
    'completed',
    'cancelled'
);

Why Does This Work?

This works because of how MySQL handles ENUM values. When performing an ALTER TABLE modification on an ENUM column, MySQL tries to map existing values based on their textual representation. If the original value does not exist in the new ENUM definition, MySQL will either throw an error (if STRICT_ALL_TABLES is enabled in sql_mode) or replace it with an empty string.

That's why it's crucial to have both old and new values present in the ENUM simultaneously during the transition phase. In our case, this ensures that every record in the database retains its exact textual equivalent. Only after executing the UPDATE queries—when we are sure that all data is using the new values—can we safely remove the old ones.

Property Hooks in PHP 8.4: Game Changer or Hidden Trap?

What if I told you your PHP objects could be cleaner, more elegant, and easier to work with? Well, that dream is now a reality! PHP 8.4 introduces revolutionary features called property hooks and asymmetric visibility that completely transform object-oriented programming as we know it. Say goodbye to clunky getters and setters – we now have a modern, intuitive way to control object data access. Let's explore how these features can revolutionize your code.

Property hooks provide a smart way to define what happens when you read from or write to object properties – and they're much cleaner and more efficient than the traditional magic methods __get/__set. Think of it as getting all the power of magic methods without any of their usual drawbacks.

Let's look at a real-world example that shows why property hooks are so valuable. Consider a common Person class with a public age property:

class Person
{
	public int $age = 0;
}

$person = new Person;
$person->age = 25;  // OK
$person->age = -5;  // OK, but that makes no sense!

While PHP ensures the age will be an integer thanks to the int type (available since PHP 7.4), what about that negative age? In the past, we'd need getters and setters, make the property private, and write a bunch of boilerplate code. With hooks, there's a much more elegant solution:

class Person
{
	public int $age = 0 {
		set => $value >= 0 ? $value : throw new InvalidArgumentException;
	}
}

$person->age = -5;  // Oops! InvalidArgumentException warns us about the invalid value

The beauty lies in its simplicity – from the outside, the property behaves exactly like before. You can read and write directly through $person->age, but now you have complete control over what happens during the write operation. And that's just scratching the surface!

We can take it further and create hooks for reading too. Hooks can have attributes, and they can contain complex logic beyond simple expressions. Check out this example of working with names:

class Person
{
	public string $first;
	public string $last;
	public string $fullName {
		get {
			return "$this->first $this->last";
		}
		set(string $value) {
			[$this->first, $this->last] = explode(' ', $value, 2);
		}
	}
}

$person = new Person;
$person->fullName = 'James Bond';
echo $person->first;  // outputs 'James'
echo $person->last;   // outputs 'Bond'

Here's something crucial to understand: hooks are always used whenever a property is accessed (even within the Person class itself). The only exception is when you directly access the actual variable inside the hook code.

A Blast from the Past: Lessons from SmartObject

For those familiar with Nette Framework, here's an interesting historical perspective. The framework offered similar functionality 17 years ago through SmartObject, which significantly enhanced object handling at a time when PHP was quite limited in this area.

I remember the initial wave of overwhelming enthusiasm where developers used properties everywhere, followed by a complete reversal where they avoided them entirely. Why? There weren't clear guidelines about when to use methods versus properties. But today's native solution is in a different league altogether. Property hooks and asymmetric visibility are fully-fledged tools that provide the same level of control as methods. This makes it much easier to determine when a property is truly the right choice.

…pokračování

The Hidden Surprises of PHP Readonly Properties

Picture this: data that's as stable as bedrock – set it once, and it stays that way forever. That's exactly what PHP 8.1 delivered with readonly properties. Think of it as giving your objects a safety vault – keeping their data secure from accidental changes. Let's explore how this powerful feature can streamline your code and what gotchas you need to watch out for.

Here's a quick taste of what we're talking about:

class User
{
    public readonly string $name;

    public function setName(string $name): void
    {
        $this->name = $name;  // First assignment - all OK
    }
}

$user = new User;
$user->setName('John');      // Great, name is set
echo $user->name;            // "John"
$user->setName('Jane');      // BOOM! Exception: Cannot modify readonly property

Once that name is set, it's locked in place. No accidental changes, no sneaky updates.

When is uninitialized really uninitialized?

Here's a common misconception: many developers think readonly properties must be set in the constructor. But PHP is actually much more flexible than that – you can set them at any point in an object's lifecycle, with one crucial rule: only once! Before that first assignment, they exist in a special ‘uninitialized’ state – think of it as a blank slate waiting for its first and only value.

Here's an interesting twist – readonly properties can't have default values. Why? Think about it: if they had default values, they'd essentially be constants – set at object creation and unchangeable from that point on.

Types are mandatory

When using readonly properties, you must explicitly declare their type. This isn't just PHP being picky – the ‘uninitialized’ state only works with typed variables. No type declaration means no readonly variable. Don't know the exact type? No worries – you can always fall back on mixed.

…pokračování


phpFashion © 2004, 2026 David Grudl | o blogu

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.