Drobné změny a opravy všeho možného v Nette
- jtousek
- Člen | 951
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)
- pekelnik
- Člen | 462
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
- 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)
- Honza Marek
- Člen | 1664
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í.
- maarlin
- Člen | 207
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
Prosím, nepište 14 věcí do jednoho vlákna.
- Doplnil jsem JsonResponse::getContentType
- po úpravě formulářů bude řešeno jinak
- problém s průhledností Image nemá. Pokud chceš ukládat průhlednost,
zavolej před save ještě
$image->savealpha(TRUE);
- není důvod, PHP má vlastní garbage collector
- 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í. - může se o tom třeba hlasovat
$form['date'] = new Date(...)
je cool. A pokud ne, je tuaddComponent
- blbost :-)
Debug::$strictMode
při výskytu chyby ukončí aplikaci, což není zrovna v produkčním režimu vítané- kdy takové situace vznikají?
- jaké čarování s foreach?
- pošli pull request
- pošli pull request
- není důvod, PHP má vlastní session garbage collector
- jtousek
- Člen | 951
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.
- Fajn.
- OK
- 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);
. - OK (Ale proč ta fce v PHP vůbec je?)
- Přesunout ImageMagick do doplňků?
- 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.
- OK
TemplateHelpers::bytes()
používá 1024. Je blbost, že by to mělo být jednotné?- 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
David Grudl napsal(a):
- pošli pull request
Napíšu až najdu trochu více času a pošlu.
- 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)