PHP triky

Weblog o elegantním programování v PHP pro mírně pokročilé

DEF CON 23

This is the first DEF CON I attended and I was wondering how a conference with no registration and just walk-ins will look like. There's probably no cap on how many people can attend and the entrance fee is very low ($230 for 4 days, compare with Black Hat at $2595 for 2 days). The conference was super crowded which ruined it for me. I was able to attend only every other talk I wanted to. You have to stay in a line for 20 to 30 minutes before a talk started to get in, at least for the popular talks. The previous talk in a different room was obviously still happening at this time meaning that you either have to leave early or attend every other talk or just stay in the same room and attend whatever is there.

I wasn't very happy about the quality of the talks either. I tried to attend talks that could be useful for my work and they were very poor – mentioning only the basic stuff, no original research, no deep insight, no new discoveries. I also attended some other talks and they were great – description of bold actions of the presenters, original research, new tools. Sadly, these talks aren't useful for my work. Let's go over the talks I attended.

Hacking web apps

★☆☆☆☆, Abstract, Outline

A very basic talk about penetration testing. The presenter mentioned some tools he use: Nessus ($2,190 per year), IBM App Scan ($18,700 per year), sqlmap (free), Xenotix (free). He also repeated several times that gathering all information is very important and mentioned KeepNote that allows him to color folders based on their severity.

Secure Messaging for Normal People

★★☆☆☆, Abstract, Slides

A very basic and vague talk about messaging apps security. The presenter didn't name a single app, he didn't even answer a direct question about which app does he personally use. He mentioned that if the communication is encrypted from you to the server and from the server to the other party then someone can still read it on the server. He also talked about metadata collection – if someone sends you a message and listens to your network then he can pair your online identity to your real identity when you download the message of a given size.

Stagefright: Scary Code in the Heart of Android

★★★★★, Abstract, no slides yet

A great talk about a recently discovered security bug in Android's media decoding library. The presenter explained how he discovered the bug (he saw lots of reports about this part of Android crashing, fuzzed it which didn't reveal any security bug but a manual inspection near the crashes revealed security issues). He also talked about privileges on Android (this component had almost all privileges, including system (just one level above root) and ability to talk with GSM. He also talked about attack vectors for this bug – the most interesting one is an MMS which is processed by this library before the notification appears. It means that you can hide the notification before it shows.

Hacking SQL Injection for Remote Code Execution on a LAMP stack

☆☆☆☆☆, Abstract, Slides

The worst talk of the conference. The presenter didn't have a good knowledge of the topic. He just mentioned bunch of random security related problems of PHP he read about at OWASP, Wikipedia and W3Schools, often unrelated to SQL injection. He mentioned Wildcard Poisioning – you create a file named -l and when someone does ls * then it actually executes ls -l – this can be used to run an arbitrary command with TAR or SCP.

REvisiting RE:DoS

★★★☆☆, Abstract, Slides

An average talk about performance problems of regular expressions – memory based in POSIX, speed based in POSIX and PCRE. The presenter showed his tool for finding the worst possible input for a given regular expression. I would expect showing more poorly performing patterns than just the obvious (a+)+. I've learned one thing at this talk: (a|ab) run against "ab" matches "a" in PCRE (the first pattern) and "ab" in POSIX (the longest pattern).

Tell me who you are and I will tell you your lock pattern

★★★★☆, Abstract, Slides

A nice talk about lock patterns that people choose. The presenter explained her own research about lock patterns people choose when asked. I was sad about two things – a small data set (only around 800 people) and not using real patterns (people were asked to create three new patterns just for this research). The research showed that almost half people start the pattern in the top left corner and almost quarter either in bottom left or top right. This is true both for left- and right-handed people. I would like to see a percentage of people using the most frequent 100 patterns but this wasn't part of the research.

I Will Kill You

★★★★★, Abstract, Slides

A very interesting talk from a guy who figured out how to issue death certificates – you only need a report from a medical examiner and funeral director. He started researching the area when a hospital mistakenly claimed 200 people as dead. It is very easy to fill the report as medical examiner – medical examiners may register online and you only need their identification number which is available online. The presenter became a real funeral director himself – he created web pages about his fake funeral agency which was enough to get the certificate. He also mentioned how easy is to issue a birth certificate – you just need a midwife instead of a funeral director. In the presentation, he showed a video how he actually “killed” someone which was very brave. He also listed why you might want to declare someone as dead (for example your parents and the judge).

Hacking Smart Safes: On the "Brink" of a Robbery

★★★★★, Abstract, no slides yet

A funny talk about hacking a very lame smart safe. The safe exposes a USB port through which you can connect a keyboard and a mouse (or Teensy sending the keyboard and mouse events for you). The safe is running Windows XP with MS Access 4 with the list of users. The hack was to escape the kiosk mode (right click on the video in tutorial, click About Flash, opens IE), open Explorer from IE, run cmd from Explorer, insert two service users into the database using VBscript and then normally log in as these users as they have the privilege to open the safe. The mitigation from the vendor was to disable IE (there are probably lots of other vectors and the main issue of unencrypted database is still there). The computer communicates with the safe through a serial port which would be probably possible to hack too.

Let's Encrypt - Minting Free Certificates to Encrypt the Entire Web

★★★★★, Abstract, no slides yet

A great talk about Let's Encrypt. They demoed the command line tool to verify the domain, issue and install the certificate and modify the config files, all in about twenty seconds. They also mentioned DVSNI – a method for validating a domain through TLS.

Protecting global email - status & the road ahead

★★★★☆, Abstract, no slides yet

A good talk about STARTTLS in SMTP. The presenter also mentioned EFF's STARTTLS Everywhere, DNSSEC and DANE. At the end of the talk, the presenter immediately started talking about a completely unrelated and unannounced topic (identifying people from keystrokes behaviors) which was quite weird. He also announced a tool to add a random delay between key strokes to prevent keyboard behavior identification.

The Bieber Project: Ad Tech 101, Fake Fans and Adventures in Buying Internet Traffic

★★★☆☆, Abstract, Slides

An average talk about creating and buying Internet traffic. The most interesting part was the explanation of a traffic exchange network Jingling which uses your computer to click at other websites and other computers click at your website in exchange – originally from China, now with users worldwide. Cheap solutions generate the traffic just from your computer which is easily filterable.

Abusing Adobe Reader’s JavaScript APIs

★★★★★, Abstract, Slides

A very insightful talk about handling JavaScript in PDF. Adobe Reader has a compiled file of a trusted JavaScript which runs without prompting the user. Luckily, someone decompiled and prettified this file. The presenters found a bug where there's a check for obj.property – they defined a getter executing an arbitrary code and called that function with this object. Then they found a function entering a privileged mode and a function calling eval on a parameter. They were able to open a URL without prompting the user. They also found an undocumented function writing anything to the filesystem. They used it to overwrite a DLL loaded by Reader and used it to run an arbitrary binary code.

Who Will Rule the Sky? The Coming Drone Policy Wars

★★★★☆, Abstract, no slides yet

A good intro to rules around drones: Anything under 55 pounds flying under 400 feet is a model plane and you don't need a license to fly it. You couldn't fly 5 miles around airports or in Washington, D.C. You couldn't shoot a drone flying over your property the same way as you couldn't burn down a neighbor's car parked in your driveway. There are state laws forbidding some usage of drones – for example you couldn't take a video of an agricultural facility from a drone in some state or you couldn't film hunters in some other state.

Knocking my neighbor’s kid’s cruddy drone offline

★★★★★, Abstract, Slides

A great talk about how to shut down a drone without firing on it. Parrot Bebop generates a WiFi network and you can connect to it. It runs several services including FTP and Telnet which are not password protected. You can connect to it and run a script (already included) to shut down the drone. It's also possible to disrupt the WiFi signal and connect to the drone from your device. It's also possible to disrupt the GPS signal but that is illegal. Bebop doesn't react to a magnetic field but Phantom 3 does.

Let's Talk About SOAP, Baby. Let's Talk About UPNP

★☆☆☆☆, Abstract, Slides

A pretty boring talk about UPnP and SOAP of smart devices. The key takeaway is that most devices will happily talk to you if you connect to them.

Jakub Vrána, Osobní, 10.8.2015, diskuse: 0 (nové: 0)

Adminer 4.2.0

Nová verze Admineru opravuje závažnou bezpečnostní chybu (XSS v přihlašovacím formuláři). Všem doporučuji okamžitou aktualizaci. Kromě toho přidává i několik novinek:

Kromě toho bylo opraveno několik dalších chyb popsaných v changes.txt.

Jakub Vrána, Adminer, 6.2.2015, diskuse: 11 (nové: 11)

Zpětná a dopředná kompatibilita

JavaScriptový kód Gmailu je poměrně velký a tak se jeho obnovení na klientech vynucuje jen každé dva týdny. Důsledkem toho je, že každá změna na serveru musí být zpětně kompatibilní – musí fungovat i s dva týdny starým klientem. Často to znamená rozdělit jednu změnu na několik menších. Pokud například chcete přejmenovat nějaký identifikátor posílaný ze serveru, musíte nejprve přidat nový, dva týdny posílat oba a teprve potom smazat ten starý. U zásadnějších změn to samozřejmě bývá složitější.

Tuto praxi bych doporučil při vývoji jakýchkoliv webových aplikací. I když nejde o dlouhoběžící aplikaci jako je Gmail, ale třeba jen obyčejnou stránku, tak vždycky může dojít k tomu, že AJAXový požadavek dostane jiná data, než s kterými počítá klient nahraný při načtení stránky. Obvykle jde jen o nepříjemný edge-case, který se dá vyřešit obnovením stránky, i tak je ale dobré se mu vyhnout. Samozřejmě není nutné čekat dva týdny, u normálních webových stránek stačí řekněme den. U déleběžících aplikací je vhodné vybudovat mechanismus, který klienta po stanovené době obnoví.

Alternativou k tomuto postupu je s každou odpovědí posílat verzi serveru a pokud je klient starší, tak ho obnovit. Pro Gmail ale tento postup není vhodný, protože obnova klienta trvá dlouho a verze se mění často. V praxi zpětně nekompatibilních změn naštěstí není moc.

Dopředná kompatibilita

V Google je vybudovaná dobrá kultura rollbacků. Když je nějaký problém (padající testy, nárůst nezachycených výjimek, pokles výkonnosti, …), tak se rychle vrátí poslední funkční verze a pak se teprve hlouběji zkoumá příčina problému a jeho řešení. Důsledkem toho je, že kód musí být i dopředně kompatibilní – musí fungovat i s budoucí verzí klienta. Může totiž dojít k tomu, že klient načte nový kód, následně dojde k rollbacku a server mu začne vracet starý formát odpovědi. U příkladu přejmenování identifikátoru je tedy potřeba i na straně klienta počítat s tím, že buď přijde jen starý identifikátor, nebo přijdou oba, nebo přijde jen ten nový.

Mimochodem ve Facebooku jsem se setkal spíše s kulturou hotfixů – když nastal nějaký problém, tak bylo snahou ho co nejrychleji identifikovat a vyřešit. Výhodou je, že vývoj jde rychleji dopředu, což je obzvlášť patrné v situaci, kdy na problematické změně závisí ještě něco dalšího. Nevýhodou je, že pokud se problém nepodaří identifikovat správně, tak se musí dělat hotfix hotfixu.

Jakub Vrána, Seznámení s oblastí, 7.12.2014, diskuse: 18 (nové: 18)

Piškvorky naslepo

Když Jirka Knesl zveřejní nějaký svůj kód, tak mám často nutkavou potřebu napsat vlastní řešení. Teď se mi to stalo s Piškvorkami naslepo. Tentokrát jsem nebyl sám, stejnou potřebu měl i Aleš Roubíček.

Přiznám se, že mi kód v Clojure přijde dost nečitelný v původní i refaktorované verzi. V PHP jsem napsal přímočaré řešení, které prostě jen řeší daný problém nejjednodušším způsobem:

<?php
echo "Ahoj v piškvorkách naslepo.
Povolené příkazy jsou:
new - nová hra
quit - konec
[a-i][0-9] - tah na pole, kde řada je pozice a, b, c, d, e, f, g, h, i. Sloupec je 1 až 9.
Formát zápisu je např. e5.
";

function isWinningInDirection(array $field, $x, $y, $a, $b) {
    // průzkum jedním směrem
    for ($i = 1; $i < 5; $i++) {
        $yb = $y - $i * $b;
        $xa = $x - $i * $a;
        if (!isset($field[$yb][$xa]) || $field[$yb][$xa] != $field[$y][$x]) {
            break;
        }
    }
    // průzkum druhým směrem
    for ($j = 1; $j < 5; $j++) {
        $yb = $y + $j * $b;
        $xa = $x + $j * $a;
        if (!isset($field[$yb][$xa]) || $field[$yb][$xa] != $field[$y][$x]) {
            return ($i + $j - 1 >= 5);
        }
    }
    return true;
}

function isWinning(array $field, $x, $y) {
    return isWinningInDirection($field, $x, $y, 1, 0)
        || isWinningInDirection($field, $x, $y, 0, 1)
        || isWinningInDirection($field, $x, $y, 1, 1)
        || isWinningInDirection($field, $x, $y, 1, -1)
    ;
}

function isFull(array $field) {
    foreach ($field as $row) {
        foreach ($row as $val) {
            if ($val == '_') {
                return false;
            }
        }
    }
    return true;
}

$playing = 'o';
while (true) {
    $field = array_fill(1, 9, array_fill(1, 9, '_'));
    while (true) {
        $playing = ($playing == 'x' ? 'o' : 'x');

        // získání vstupu od uživatele
        while (true) {
            echo "Hráč $playing: ";
            $input = rtrim(fgets(STDIN));
            if ($input == "new") {
                break 2;
            } elseif ($input == "quit") {
                echo "Naviděnou.\n";
                exit;
            }
            $y = ord(substr($input, 0, 1)) - ord('a') + 1;
            $x = substr($input, 1, 1);
            if (strlen($input) != 2 || !isset($field[$y][$x])) {
                echo "Tah ve špatném formátu.\n";
            } elseif ($field[$y][$x] != '_') {
                echo "Pole je zabráno, hraj znovu.\n";
            } else {
                break;
            }
        }
        $field[$y][$x] = $playing;

        if (isWinning($field, $x, $y)) {
            echo "VÝHRA! Gratulace hráči $playing.\n";
            break;
        } elseif (isFull($field)) {
            echo "Remíza, hrací pole zaplněno.\n";
            break;
        }
    }

    // zobrazení hracího pole
    foreach ($field as $row) {
        foreach ($row as $val) {
            echo "$val ";
        }
        echo "\n";
    }
    echo "Nová hra.\n";
}
?>

Na mé verzi kódu mi nejvíc vadí tři do sebe zanořené smyčky while (true). Víc by se mi líbilo, když by to byly do-while smyčky, ze kterých by bylo jasně patrné, čím končí. To by ale zase kód zkomplikovalo jiným způsobem. Získání vstupu od uživatele vyloženě volá o přesunutí do funkce – k tomu bych přistoupil, pokud by kód byl ve třídě, v současné verzi by předávání stavu bylo dost komplikované.

V hlavním kódu, který komunikuje s uživatelem, je i část herní logiky – konkrétně inicializace pole, změna hráče, zápis tahu a volání testů na remízu a výhru. Nevidím to ale jako velký problém – pokud bych hru chtěl udělat třeba pro prohlížeč, tak bych určitě využil funkce isWinning a isFull, ale zbytek bych napsal znovu. Přiznávám, že abstrahovat hru tak, aby se dala napojit jak na konzoli, tak třeba na web, by dalo dost práce.

Jakub Vrána, Řešení problému, 25.11.2014, diskuse: 11 (nové: 11)

Co se mi nelíbí na JavaScriptu

Článek vyšel na serveru Zdroják.

Když jsem v 90. letech s JavaScriptem začínal, tak jsem ten jazyk nesnášel. Hlavní důvod byl ale ten, že co jsem napsal pro jeden prohlížeč, nefungovalo v druhém. Když jsem pochopil, že to není až tak problém jazyka, ale rozdílného API v prohlížečích, tak jsem si JavaScript postupně začal oblibovat, až se stal mým druhým nejoblíbenějším jazykem. Navíc je mnohem elegantněji navržený než PHP a temných zákoutí je v něm mnohem míň.

Na JavaScriptu se mi nejvíc líbí myšlenka, že funkce je úplně obyčejná hodnota podobně jako třeba řetězec – dá se kdekoliv definovat, předávat, není na ní zkrátka nic zvláštního. Většina ostatních jazyků, které znám, považuje funkce za něco speciálního a terpve v poslední době umožňuje funkce vytvářet a používat na místech, kde se dá pracovat s jakýmikoliv jinými hodnotami.

Část věcí, které se mi na JavaScriptu nelíbí, je nejspíš dána tím, že byl navržen za 10 dní před téměr 20 lety a od té doby se prakticky nezměnil. Část je ale také dost možná jiná filozofie.

Nutnost psát var

Pro vytvoření lokální proměnné je potřeba použít klíčové slovo var. Na tom by nebylo nic až tak špatného, pokud by jeho neuvedení neznamenalo, že pracujete s globální proměnnou. Chování v PHP, kde se defaultně pracuje s lokálními proměnnými a pokud chcete globální, tak musíte použít global, mi přijde mnohem lepší.

Řekl bych, že to je do značné míry vynuceno vlastností, že funkce jsou normální hodnoty. Když v JavaScriptu voláme funkci f, musí se najít proměnná f a zkusit se zavolat hodnota, která je v ní uložená. Pokud by práce s globálními proměnnými byla explicitní, tak bych vždy musel uvést, že chci volat globální f a ne tu lokální.

Function scope proměnných

Block scope je mnohem přirozenější a příjemnější na používání než function scope, kromě toho vede k méně chybám. Navíc v JavaScriptu všechny lokální proměnné vzniknou hned na začátku funkce, takže následující kód ja platný:

function () {
	alert(a); // Zobrazí undefined
	var a = 'Ahoj';
}

Pokud navíc v jedné funkci stejnou proměnnou deklarujete vícekrát (např. dva cykly ve tvaru for (var i = 0; ; )), tak vám nástroje jako JSHint vyhubují. Abyste se tomu vyhnuli a abyste zdůraznili okamžik vzniku proměnných, bylo by nejlepší všechna var napsat hned na začátek funkce, to je ale pořádný opruz.

ECMAScript 6 zavádí block scope pomocí klíčového slova let, v prohlížečích ho ale stejně nepůjde dalších několik let používat kvůli kompatibilitě.

Proměnná arguments

Proměnná arguments, která automaticky vzniká při volání funkce, sice vypadá jako pole, ale ve skutečnosti to pole není, takže na ni nejdou přímo používat metody pole. Je to vynuceno nejspíš tím, že obsah proměnné je svázán s parametry funkce – pokud uvnitř funkce do parametru přiřadíme nějakou hodnotu, projeví se to i v arguments a naopak. To ostatně považuji také za dost pochybnou vlastnost, která může kód spíš znepřehlednit, než že by mu nějak pomohla.

Osobně bych se klonil k tomu, aby funkce s nepojmenovanými parametry vůbec nemohla pracovat a aby volání funkce s více parametry, než kolik jich má, skončilo chybou. Vytvořit pole je v JavaScriptu velmi snadné, takže předat pole hodnot je téměř stejně snadné jako předat hodnoty v parametrech. Naopak to vede k přehlednějšímu API a někdy i přehlednějšímu kódu (protože není třeba používat Function.apply, ale stačí funkci rovnou zavolat).

ECMAScript 6 situaci trochu vylepšuje operátorem spread.

Iterace polí

Nejspolehlivější způsob iterace polí je pomocí for cyklu procházejícího od nuly do array.length. Je to zbytečně krkolomné, index prvku mě často vůbec nezajímá, počet prvků taky zjišťovat nechci. Navíc k iterovanému prvku musím přistupovat přes pole a aktuální index. Pokud chci navíc procházet výsledek funkce, musím si ho uložit do dočasné proměnné. Kód pak vypadá nějak takhle:

var tags = getTags();
for (var i = 0; i < tags.length; i++) {
	var tag = tags[i];
	// tady bude kód pracující s tag
}

Oč jednodušší to je v PHP:

<?php
foreach (getTags() as $tag) {
    // tady bude kód pracující s $tag
}
?>

V ECMAScript 5.1 jde používat array.forEach, ale ani to se mi kvůli API používajícímu callback nezdá jako nejlepší řešení.

Iterace objektů

Iteraci objektů taky nepovažuji za ideální především proto, že se prochází i uživatelem definované vlastnosti na prototypu. Takže pokud nějaký chytrák definuje třeba Object.prototype.clone, tak bude clone strašit při iteraci všech objektů. Jako obranu byste ve všech iteracích měli používat hasOwnProperty:

for (var key in map) {
	if (map.hasOwnProperty(key)) {
		var value = map[key];
		// tady bude kód pracující s value
	}
}

Sám jsem naštěstí závislost na cizím kódu hackujícím Object.prototype řešit nemusel, takže hasOwnProperty nepoužívám a problém mě tolik netrápí. I když iterace výhradně přes klíče a nutnost hodnotu získat mě taky obtěžuje. Oč jednodušší je PHP verze:

<?php
foreach ($map as $value) {
    // tady bude kód pracující s $value
}
?>

Objekt je HashMap, nikoliv LinkedHashMap

Pole v PHP dovoluje přistoupit k prvku podle klíče v konstantním čase a zároveň ručí za pořadí při procházení. V Java terminologii jde o LinkedHashMap. JavaScript kupodivu za pořadí při procházení objektu neručí (jde tedy o HashMap), i když ho prohlížeče až na některé okrajové případy dodržují. Donedávna jsem o téhle záludnosti neměl tušení. Pokud potřebujete pořadí dodržet, můžete si klíče ukládat do pole, které budete používat při iteraci.

Čárka za posledním prvkem

Za posledním prvkem pole nelze psát čárku. Tedy – už ECMAScript 3 to umožňuje, ale Internet Explorer to měl špatně, takže [0,].length všude vrací 1, ale IE<9 vrací 2. Za posledním prvkem objektu jde psát čárku až od ECMAScript 5, kvůli kompatibilnímu režimu prohlížečů to tedy půjde univerzálně používat až za několik let.

Tento nedostatek vadí především u diffů větších polí a objektů formátovaných na více řádek. Kdykoliv na tohle narazím, tak si představuji, jak to mohlo vypadat v květnu 1995: „Hele, parser nějak funguje, zítra to odevzdávám, trailing comma tam doplním v další verzi.“

Nepovinný středník

Automatické vkládání středníku na nahodilá místa také nepovažuji za právě povedenou vlastnost. Co myslíte, že vrátí následující funkce?

function () {
	return
	{
		x: 2
	}
}

Uhodli jste, že undefined? Za return si totiž JavaScript domyslí středník a následující objekt pochopí jako blok s návěštím x a kódem 2 (za kterým si taky domyslí středník).

V praxi se s tímto problémem člověk naštěstí často nesetká, dnes a denně na něj ale narážím prostřednictvím Google JavaScript Style Guide, který kvůli vkládání středníků vyžaduje zalamování dlouhých řádků za operátorem, nikoliv před ním (jak jsem na to zvyklý jinde).

Prototypová dědičnost

Nápad dědit z vytvořených objektů místo ze tříd jsem nikdy nevzal zasvé. Když už pominu paměť zbytečně alokovanou při vytváření objektu přiřazovaného do prototypu potomka, jak asi nastavím parametry konstruktoru předka, když ty budu znát až v konstruktoru potomka, aha? ECMAScript 5.1 dovoluje vytvořit prázdný objekt předka pomocí Object.create, sám používám goog.inherits nebo obdobu, což do prototypu přiřadí prázdný objekt, jehož prototyp nastaví na prototyp předka.

Přepisování metod objektu

Hrůzou mi vstávají vlasy na hlavě při představě, že zavolám nějakou cizí funkci, a ona mi pod rukama změní metody mého objektu – třeba je vymění za něco jiného. V testech se to může hodit, ale v běžném životě to nepřináší nic dobrého. Navíc nejsem nijak chráněn proti překlepům – když něco přiřadím do this.itme místo this.time, tak se o tom nijak nedozvím.

ECMAScript 5.1 nabízí řešení v podobě Object.freeze a Object.seal, to je ale poměrně krkolomné na používání:

function Square(x) {
	this.x = x;
}
Square.prototype = Object.freeze({
	getArea: function () {
		return this.x * this.x;
	}
});
var square = Object.seal(new Square(2));
console.log(square.getArea());
square.x = 3;
console.log(square.getArea());

Object.freeze způsobí, že vlastnosti nejde přidávat, měnit ani mazat – to chceme typicky u prototypu. Object.seal způsobí, že vlastnosti nejde přidávat a mazat, pořád jdou ale měnit – to chceme typicky u vlastností definovaných v konstruktoru.

Předávání this

Funkce jako first-class citizen miluji, dojem ale poněkud kazí chování this. Jde o to, že když někam předáte funkci, tak její this bude nějaký jiný, typicky globální objekt. To je potřeba zcela ojediněle a případně se to dá triviálně vyřešit předáním onoho objektu jako parametru funkce. Lépe by mi vyhovovalo, když by this bylo nastaveno na objekt, přes který jsme k metodě přistoupili, případně na aktuální this uvnitř closure. V praxi je pak kód zaset spoustou zbytečných Function.bind, které jen nastavují this na this.

S tímto nedostatkem souvisí i to, že vaší metodě může někdo nastavit this na libovolný objekt.

Dvě prázdné hodnoty

V JavaScriptu jsou podle mě úplně zbytečně dvě různé hodnoty, které znamenají „nic“ – null a undefined. Obě bohužel potřebujete, protože undefined mají proměnné, dokud do nich nic nepřiřadíte, a null vrací některé funkce, např. prompt. V Closure anotacích pak lze najít nádhery jako /** @type {?number|undefined} */, kde ? znamená null a undefined znamená undefined.

Ostatní jazyky, které používám, si vystačí s jednou prázdnou hodnotou. I když chápu akademický smysl dvou různých prázdných hodnot, tak v praxi je to k ničemu a akorát to otravuje život.

Chybějící operátor list

Občas mi chybí operátor list dovolující přiřadit více proměnných najednou. Ale je to vlastně jen po volání regExp.exec. Většinou absence této konstrukce vede jen k tomu, že funkce vracející více hodnot vrací objekty a ne pole, což je přehlednější. V ECMAScript 6 to řeší destructuring assignment.

Řetězce v apostrofech nebo uvozovkách

Dřív mi vyhovovalo, že hodnoty atributů v HTML, řetězce v PHP, řetězce v JavaScriptu a dokonce i řetězce v MySQL se dají uzavírat jak do uvozovek, tak do apostrofů. Časem mi ale začala nejednotnost vadit víc než to, že občas ušetřím pár zpětných lomítek. V PHP aspoň uvozovky a apostrofy znamenají dvě různé věci, v JavaScriptu bych uvozovky využil na něco jiného – ideálně na regulární výrazy, jejichž uzavírání do lomítek ztěžuje takové to domácí parsování JavaScriptového kódu.

V praxi na kód s uvozovkami naštěstí díky coding style nenarážím.

Callback hell

Krása konceptu předávání funkcí zavolaných po dokončení asynchronní operace se bohužel poněkud rozmělňuje nutností do sebe tyto callbacky hluboko zanořovat. Vede to ke kódu mnohem nepřehlednějšímu než když je psaný sekvenčně. Sám na to naštěstí tolik nenarážím, protože na klientské straně na sebe asynchronní operace většinou moc často nenavazují. Ale je to jeden z důvodů, proč mě příliš neláká node.js.

Ve Facebooku se dal asynchronní kód psát sekvenčně díky operátoru yield, který bude i v ECMAScriptu 6. Osobně by se mi ale ještě víc líbilo async/await, což je ale hudba vzdálené budoucnosti, pokud vůbec nějaké.

Závěr

JavaScript považuji za dobrý jazyk. Kromě zmíněných hodnot typu funkce se mi líbí třeba i objektové literály nebo fakt, že kód běží v jednom vlákně, i když je celkem běžně asynchronní – kód a uvažování o jeho chování to dramaticky zjednodušuje a žádné výrazné problémy to nezpůsobuje. Perfektní je i to, že || vrátí první truthy prvek. Vím, že některé nedostatky by mi pomohl překlenout CoffeeScript nebo jiný transpiler, na JavaScriptu mi ale vyhovuje hustota informací ve zdrojovém kódu, kterou považuji u CoffeeScriptu za příliš vysokou. Ze stejného důvodu jsem si nikdy neoblíbil Perl – přišel mi příliš nepřehledný, zato kód v PHP jsem chápal, aniž bych se jazyk musel učit. Java je pro mě zase příliš řídká. S některými věcmi pomáhá Closure Library a Closure Compiler, ale milejší by mi bylo, když bych na nich nemusel záviset. TypeScript mi ve srovnání s možnostmi ekosystému Closure přijde jako chudý příbuzný.

Sérié článků „Co se mi nelíbí“ na Javě, na Go, na JavaScriptu nasvědčuje tomu, že hledám jazyk, ve kterém by se mi programovalo stejně pohodlně jako v PHP (nebo pohodlněji). JavaScript tím jazykem, který bych bez přemýšlení použil na cokoliv, bohužel také není.

Přijďte si o tomto tématu popovídat na školení JavaScript a AJAX.

Jakub Vrána, Osobní, 20.11.2014, diskuse: 0 (nové: 0)

Starší články naleznete v archivu.

avatar © 2005-2015 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.