Drobné změny a opravy všeho možného v Nette

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
jtousek
Člen | 951
+
0
-

Předem se omlouvám za pravopisné chyby. Je toho hodně a už jsem neměl sílu to po sobě číst.

Bylo by asi nejlepší, kdybych rovnou posílal pull requesty na GitHub, bohužel ale s gitem ještě neumím.

1. JsonReponse contentType

Kdysi dávno jsem potřeboval u třídy JsonResponse drobně upravit metodu send, ale nešlo to řešit voláním parent::send();. Narazil jsem na problém, že vlastnost contentType je private a navíc nemá getter, takže ji namohu odeslat jako hlavičku. Ve výsledku jsem si tedy JsounResponse zkopíroval a svou logiku dopsal přímo, což není ideální.

Řešení: označit jako protected nebo doplnit getter (u DownloadResponse tento getter je).

2. Abstraktní ListControl pro formuláře

V podstatě jde o to, že třídy RadioList a CheckboxList z addons mají většinu kódu úplně stejnou, + si možná ještě dopíšu totéž pro 3-state checkboxy. Bylo by dobré aby se v nových fomulářích objevila nějaká abstraktní třída, od které by dědil RadioList a kterou by mohl kdokoli rozšířit.

3. Nette\Image PNG průhlednost

Docela jsem se divil, že dokonce i v Nette je problém s průhledností PNG obrázků. Takže tady je fix, který opraví resize částečně poloprůhlendých PNG:

<?php
//třída Nette\Image

	/**
	 * Creates blank image.
	 * @param  int
	 * @param  int
	 * @param  array
	 * @return Image
	 */
	public static function fromBlank($width, $height, $color = NULL)
	{
		if (!extension_loaded('gd')) {
			throw new \Exception("PHP extension GD is not loaded.");
		}

		$width = (int) $width;
		$height = (int) $height;
		if ($width < 1 || $height < 1) {
			throw new \InvalidArgumentException('Image width and height must be greater than zero.');
		}

		$image = imagecreatetruecolor($width, $height);
		if (is_array($color)) {
			$color += array('alpha' => 0);
			$color = imagecolorallocatealpha($image, $color['red'], $color['green'], $color['blue'], $color['alpha']);
			imagealphablending($image, FALSE);
			imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $color);
			imagealphablending($image, TRUE);
			imagesavealpha($image, TRUE); //TENTO ŘÁDEK JSEM PŘIDAL
		}
		return new static($image);
	}

?>

Podobný fix je potřeba i u metody Nette\Image::fromFile (jinak prosté otevření a uložení PNG obrázku zničí průhlednost). V případě PNG stačí opět zavolat funkci imagesavealpha, u JPG to nemá smysl, jak je to s průhledností GIFu jsem nezkoušel (proto fix neuvádím).

Možná by totéž mělo být i u metody fromString, tam jsem to ale nepotřeboval, byť ji používám.

Ještě drobná poznámka. Metodu sharpen doporučuji používat jen výjimečně. Jednak likviduje průhlednost PNG a jednak mi JPEGy bez sharpen stejně připadají lepší.

4. Ještě jednou Nette\Image

Je to drobnost, ale myslím, že by se do Nette\Image měl přidat tento destruktor:

<?php
	public function __destruct() {
		return imagedestroy($this->image);
	}
?>

5. Nette\Image potřetí

Nějak se mi nechce líbit třída ImageMagick. Konkrétně to, že rozšiřuje třídu Image – vždyť metody Nette\Image pracijí s resource takže ty, které zdědí a nepřepíše nemohou fungovat, ne? Byl bych spíše proto aby obě dvě implementovaly stejné rozhraní anbo rozšiřovaly jednu abstraktní třídu. Stejně tak statická $useImageMagick mi do Nette\Image nepasuje.

Čistě teoreticky by se třída Nette\Image měla přejmenovat na GdImage nebo ImageGd, protože pracuje s knihovnou GD, ale implementovat by to šlo i mnoha jinými způsoby. (Pochybuju, že zrovna tuhle změnu komunita přijme, ani mě se moc nelíbí, ale teoreticky by to tak být mělo, ne?)

6. Nette\Object write-only property

Ne, nedám s tím pokoj. :)

Přestože zde už padly návrhy na úplné zrušení properties, myslím, že zejména pro šablony jsou properties velmi dobré. Ale výrazně mi pomohly v jiných případech:

<?php

/**
 * @Entity
 */
class FooEntity extends Nette\Object {

    //$id jako primary key

    /**
     * @OneToMany(targetEntity = "BarEntity", mappedBy = "foo")
     * @var Doctrine\Common\Collections\Collection
     */
    private $bars;

    /**
     * @return Doctrine\Common\Collections\Collection
     */
    public function getBars() {
        return $this->bars;
    }

}

//použití
$foo = new FooEntity;
$bar = new BarEntity;
//díky Nette properties:
$foo->bars[] = $bar;
//bez properties díky chytrým kolekcím Doctrine:
$foo->getBars()->add($bar);
//ale bez properties kdyby metoda add neexistovala (což hloupé implementace ArrayAccess jako třeba Nette\Forms\FormContainer dělají)
$bars = $foo->getBars();//FUJ!
$bars[] = $bar;

?>

Když jsem si uvědomil tohle, začal jsem propeties kromě šablon používat i při práci nad entitami Doctrine. Abych to měl jednotně tak všude, nejen v případě kolekcí. Najednou jsem ale zjistil, že uživatelům se neukládá heslo a naprosto jsem netušil proč to tak je. Trvalo dobrou půlhodinu než jsem zjistil, že Nette\Object write-only property nepodporuje (password mělo z pochopitelných důvodů jenom setter).

Jak zde již zaznělo, toto chování je magie a není naprosto očekávané. Když už je v Nette něco tak magického jako properties a má to své využití, tak ať se to alespoň chová transparentně, jak člověk programátor očekává.

7. Metoda add pro FormContainer

Jak jsem se zmínil o kousek výše, každá implementace ArrayAccess by měla mít nějakou metodu add. Třída FormContainer jich má celou hromadu, ale všechny specializované, přičemž všechny až na addHidden a addPassword jen tupě přidají nový prvek, ale obecná metoda add, přes kterou by šlo přidávat vlastní formulářové prvky chybí. To vede k tomu, že se ke každému FormControl v addons nebo co si člověk dopíše přidává další podobná metoda jako třeba addDatePicker pomocí extensionMethod (což opravdu nemám rád).

Mnohem víc se mi líbí přístup Symfony, která nové formulářové prvky přidává právě přes add(new Date(...)). Ano mohl bych využít i $form['date'] = new Date(...);, ale opravdu se zrovna tohle u formulářů někomu líbí?

Jsem tedy pro přidání jednocuché metody add do třídy Nette\Forms\FormContainer:

<?php

	/**
	 * Adds control to the form.
	 * @param  string  control name
	 * @param  Nette\Forms\IFormControl  control
	 * @return Nette\Forms\IFormControl
	 */
	public function add($name, $control)
	{
		return $this[$name] = $control;
	}
?>

//EDIT: Metody addComponent jsem si dříve nevšiml, je to přesně to, co chci, jen mi trochu vadí obrácené pořadí parametrů. Popravdě jsem stále pro přidání jednouchého add jen pro IFormControl.

8. Drobná změna DebagBaru

Konkrétně mi jde o Nette/Debug/templates/bar.memory.tab.phtml. Zobrazování využité paměti je fajn věc, ale trošku bych to upravil. Takhle vypadá původní kód:

<?php echo function_exists('memory_get_peak_usage') ?
number_format(memory_get_peak_usage() / 1000, 1, '.', ' ') : 'n/a'; ?> kB

  • volat memory_get_peak_usage(TRUE) pro reálně použitou paměť
  • jako programátor s tím / 1000 nemohu souhlasit, to používají výrobci pevných disků aby měly větší kapacity ⇒ / 1024
  • tohle už je k diskusi, ale raději bych hodnotu zobrazoval v MB na 2 desetinná místa

Výsledný kód:

<?php echo function_exists('memory_get_peak_usage') ?
number_format(memory_get_peak_usage(TRUE) / 1024 / 1024, 2, '.', ' ') : 'n/a'; ?> MB

//EDIT:

9. Při Debug::PRODUCTION nefunguje strictMode

Už nějakou dobu jsem se hrozně divil, že mě laděnka neupozornila na určité chyby, které jsem později náhodou našel. Typicky nedefinovanou konstantu, když jsem někde omylem umazal dolar. Nyní jsem zjistil, že je to kvůli tomuto commitu.

Jde o to, že na testovacím serveru potřebuju chyby logovat (když testují kolegové), DEVELOPMENT nepoužívám, protože to hází chyby přímo na výstup, to ale nechci, neboť o vizualizaci se starám sám pomocí Debug::$onFatalError[] = 'funkce'. Používám tedy PRODUCTION a potřebuji aby to chytalo a logovalo i tyhle chyby, což nyní nelze.

Jaké má tohle chování opodstatnění? Vyšší úroveň hlášení chyb přeci není nijak závislá na tom, zda se jedná o development nebo production. Pokud nechci tyto chyby na produkci zachytávat, tak je prostě vypnu. Takhle je ale nemohu zapnout, což je dle mého názoru chyba.

//EDIT: Když by se toto opravilo, aktuálního chování stále lze docílit takto:

<?php
Debug::enable();
if (!Debug::$productionMode) Debug::$strictMode = TRUE;
?>

Editoval jtousek (26. 11. 2010 20:24)

Foowie
Člen | 269
+
0
-

ad 6) ++
ad 7) addComponent ?
ad 9) ++

Editoval Foowie (26. 11. 2010 20:18)

jtousek
Člen | 951
+
0
-

Foowie: máš pravdu, addComponent jsem si nevšiml, jen mi trochu vadí obrácené pořadí parametrů.

pekelnik
Člen | 462
+
0
-

Souhlasím se všema devíti bodama.

a přidal bych dva vlastní – nebojte – ve stejném duchu, tedy

10) setter pro $presenter->ajaxMode

Někdy se prostě hodí nastavit, že request je „ajaxový“ ač si to framework nemyslí (není přítomná žádná z „ajaxových“ hlaviček)

Nette\Application\Presenter:

+ public function setAjaxMode($mode)
+ {
+ 	$this->ajaxMode = $mode;
+ }

v současné době tam mám reflexi což se mi moc nepozdává… alternativně by ta proměnná mohla být protected

11) setter pro $arraylist->list

Nette\ArrayList:

+ public function setList($list)
+ {
+ 	$this->list = $list;
+ }

Ulehčilo by to práci s routerem nemusely by se dělat šaškárny s foreachema

Editoval pekelnik (26. 11. 2010 23:20)

maarlin
Člen | 207
+
0
-
  • 3) ++
  • 4) ++ (skoro se divím, že tam doteď ten destruktor nebyl…) :))
  • 8) O tom už se vedla diskuze
  • 9) += 100 – rozhodně souhlasím – evidentně to vyřeší i tento problém

A následuje krátký seznam mých feature request (záměrně nepíšu všechny, ale jen ty, co mě pálí nejvíc):

12) Řazení dotazů v dibipanelu laděnky + původ SQL dotazu (model/metoda)

https://forum.nette.org/…azeni-dotazu

13) Umožnění ukládání do MemCached s kompresí

https://forum.nette.org/…staveni-flag

14) Garbage collector pro sessions uložené ve filesystému

Editoval maarlin (28. 11. 2010 21:48)

jtousek
Člen | 951
+
0
-

pekelnik: Díky. :)

  • 10) ++ už jsem na to také narazil

maarlin:

  • 8) V tom případě je tu další důvod, TemplateHelpers::bytes používá 1024. Když nic jiného mělo by to být jednotné a jako programátor hlasuji pro 1024.
  • 14) ++

Editoval jtousek (28. 11. 2010 22:31)

Honza Marek
Člen | 1664
+
0
-

To je fakt ideální zabalit 14 feature requestů do jednoho vlákna na fóru. Přehlednost je skvělá.

Za to vás odměním názorem, že properties by neměly existovat. Jen šablona by měla umět přečíst $něco->věc jako $něco->getVěc().

jtousek napsal(a):

Bylo by asi nejlepší, kdybych rovnou posílal pull requesty na GitHub

A s tímhle nelze než souhlasit. Samozřejmě ty kontroverzní návrhy by ti neprošly.

bohužel ale s gitem ještě neumím.

Změny jednoho souboru lze udělat na githubu jen přes webové rozhranní.

jtousek
Člen | 951
+
0
-

Honza Marek napsal(a):

To je fakt ideální zabalit 14 feature requestů do jednoho vlákna na fóru. Přehlednost je skvělá.

Tak sorry že nechci zaspamovat fórum devíti vlákny.

maarlin
Člen | 207
+
0
-

Honza Marek napsal(a):

To je fakt ideální zabalit 14 feature requestů do jednoho vlákna na fóru. Přehlednost je skvělá.

Faktem je, že co se týká těch mých, mají už své samostatné vlákno, toto vlákno jsem chápal spíše jako takové jejich vypíchnutí, resp. vybrání toho, co mě fakt nejvíc pálí… existuje pochopitelně spousta dalších věcí, které bych chtěl opravit, nebo prostě přidat do Nette, ale nejsou natolik zásadní… Pokud jsem špatně pochopil účel tohoto vlákna, omlouvám se… zabte mě :)

David Grudl
Nette Core | 8218
+
0
-

Prosím, nepište 14 věcí do jednoho vlákna.

  1. Doplnil jsem JsonResponse::getContentType
  2. po úpravě formulářů bude řešeno jinak
  3. problém s průhledností Image nemá. Pokud chceš ukládat průhlednost, zavolej před save ještě $image->savealpha(TRUE);
  4. není důvod, PHP má vlastní garbage collector
  5. ImageMagick je trošku magick, hodí se ke změně rozměrů nebo oříznutí velkých obrázků a všechny ostatní operace už provádí přes GD. $useImageMagick se mi taky nelíbí.
  6. může se o tom třeba hlasovat
  7. $form['date'] = new Date(...) je cool. A pokud ne, je tu addComponent
  8. blbost :-)
  9. Debug::$strictMode při výskytu chyby ukončí aplikaci, což není zrovna v produkčním režimu vítané
  10. kdy takové situace vznikají?
  11. jaké čarování s foreach?
  12. pošli pull request
  13. pošli pull request
  14. není důvod, PHP má vlastní session garbage collector
jtousek
Člen | 951
+
0
-

David Grudl napsal(a):

Prosím, nepište 14 věcí do jednoho vlákna.

OK, omlouvám se příště to rozdělím.

  1. Fajn.
  2. OK
  3. Když načtu PNG, který používá průhlednost do Nette\Image a hned jej zase uložím bez jakékoli změny, průhlednost se ztratí. Považuji to za chování, které je žádoucí jen v opravdu výjimečných případech. Byl bych spíše kdyby se pro případ nezachování průhlednosti mělo volat $image->savealpha(FALSE);.
  4. OK (Ale proč ta fce v PHP vůbec je?)
  5. Přesunout ImageMagick do doplňků?
  6. Ve všech tématech, kde se to řešilo jsi byl se svým názorem pokud vím jediný. Většině to bylo jedno a zbytek byl proti.
  7. OK
  8. TemplateHelpers::bytes() používá 1024. Je blbost, že by to mělo být jednotné?
  9. S tím souhlasím, ale toho chování lze docílit aniž by to bylo takhle striktně zakázané. Naopak tento zákaz je omezující. To, že to laděnka začala takhle najednou zakazovat je nečekané a magické chování.
maarlin
Člen | 207
+
0
-

David Grudl napsal(a):

  1. pošli pull request

Napíšu až najdu trochu více času a pošlu.

  1. pošli pull request

Pull request odeslán. Spíš jsem ještě přemýšlel, jestli tam neudělat nějakou property alá MemcachedStorage::$permanentFlag; , která by se automaticky doplňovala do všech Memcache::get, set a replace…

Editoval maarlin (5. 12. 2010 22:28)