Merge symfony/master

This commit is contained in:
Benjamin Eberlei 2011-09-04 20:25:40 +02:00
commit 21b29c201b
1442 changed files with 24781 additions and 14213 deletions

45
CHANGELOG-2.0.md Normal file
View File

@ -0,0 +1,45 @@
CHANGELOG for 2.0.x
===================
This changelog references the relevant changes (bug and security fixes) done
in 2.0 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.0.0...v2.0.1
* 2.0.1 (2011-08-26)
* 1c7694f: [HttpFoundation] added a missing exception
* 84c1719: [FrameworkBundle] Avoid listener key conflicts in ContainerAwareEventDispatcher
* 536538f: [DoctrineBundle] removed an unused and confusing parameter (the connection class can be changed via the wrapper_class setting of a connection)
* d7f0789: [FrameworkBundle] fixed duplicated RequestContext instances
* 89f477e: [WebProfilerBundle] Throw exception if a collector template isn't found
* 6ca72cf: [WebProfilerBundle] Allow .html.twig in collector template names
* 39fabab: [EventDispatcher] Fix removeSubscriber() to work with priority syntax
* 3380f2a: [DomCrawler] fixed disabled fields in forms (they are available in the DOM, but their values are not submitted -- whereas before, they were simply removed from the DOM)
* 2b1bb2c: [Form] added missing DelegatingValidator registration in the Form Extension class (used when using the Form component outside a Symfony2 project where the validation.xml is used instead)
* fdd2e7a: [Form] Fixing a bug where setting empty_value to false caused a variable to not be found
* bc7edfe: [FrameworkBundle] changed resource filename of Japanese validator translation
* c29fa9d: [Form] Fix for treatment zero as empty data. Closes #1986
* 6e7c375: [FrameworkBundle] Cleanup schema file
* b6ee1a6: fixes a bug when overriding method via the X-HTTP-METHOD-OVERRIDE header
* 80d1718: [Fix] Email() constraints now guess as 'email' field type
* 3a64b08: Search in others user providers when a user is not found in the first user provider and throws the right exception.
* 805a267: Remove Content-Length header adding for now. Fixes #1846.
* ae55a98: Added $format in serialize() method, to keep consistence and give a hint to the normalizer.
* 7ec533e: got an if-condition out of unnecessary loops in Symfony\Component\ClassLoader\UniversalClassLoader
* 34a1b53: [HttpFoundation] Do not save session in Session::__destroy() when saved already
* 81fb8e1: [DomCrawler] fix finding charset in addContent
* 4f9d229: The trace argument value could be string ("*DEEP NESTED ARRAY*")
* be031f5: [HttpKernel] fixed ControllerResolver when the controller is a class name with an __invoke() method
* 275da0d: [Validator] changed 'self' to 'static' for child class to override pattern constant
* e78bc32: Fixed: Notice: Undefined index: enable_annotations in ...
* 86f888f: fix https default port check
* 8a980bd: $node->hasAttribute('disabled') sf2 should not create disagreement between implementation and practice for a crawler. If sahi real browser can find an element that is disabled, then sf2 should too. https://github.com/Behat/Mink/pull/58#issuecomment-1712459
* 1087792: -- fix use of STDIN
* ee5b9ce: [SwiftmailerBundle] Allow non-file spools
* d880db2: [Form] Test covered fix for invalid date (13 month/31.02.2011 etc.) send to transformer. Closes #1755
* df74f49: Patched src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php to throw an exception when an invalid date is passed for transformation (e.g. 31st February)
* 8519967: Calling supportsClass from vote to find out if we can vote
* 2.0.0 (2011-07-28)

33
CHANGELOG-2.1.md Normal file
View File

@ -0,0 +1,33 @@
CHANGELOG for 2.1.x
===================
This changelog references the relevant changes (bug and security fixes) done
in 2.1 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.1.0...v2.1.1
2.1.0
-----
### ClassLoader
* added support for loading globally-installed PEAR packages
### Finder
* Finder::exclude() now supports an array of directories as an argument
### HttpFoundation
* added support for the PATCH method in Request
* removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3
* added ResponseHeaderBag::makeDisposition() (implements RFC 6266)
### Translation
* added dumpers for translation catalogs
### Validator
* added support for MIME with wildcard in FileValidator

281
CONTRIBUTORS.md Normal file
View File

@ -0,0 +1,281 @@
CONTRIBUTORS
============
Symfony2 is the result of the work of many people who made the code better
(see http://symfony.com/contributors for more information):
- Fabien Potencier (fabpot)
- Bernhard Schussek (bschussek)
- Johannes (schmittjoh)
- Kris Wallsmith (kriswallsmith)
- Victor Berchet (vicb)
- Jordi Boggiano (Seldaek)
- Ryan Weaver (weaverryan)
- Christophe Coevoet (stof)
- Pascal Borreli (pborreli)
- Joseph Bielawski (stloyd)
- Igor Wiedler (igorw)
- Benjamin Eberlei (beberlei)
- Lukas Kahwe Smith (lsmith77)
- Jonathan H. Wage (jwage)
- Hugo Hamon (hhamon)
- Jeremy Mikola (jmikola)
- Thibault Duplessis (ornicar)
- Bulat Shakirzyanov (avalanche123)
- Eriksen Costa (eriksencosta)
- Francis Besset (francisbesset)
- yethee (yethee)
- Miha Vrhovnik (mvrhov)
- Konstantin Kudryashov (everzet)
- Martin Hasoň (hason)
- Henrik Bjørnskov (henrikbjorn)
- Lenar Lõhmus (lenar)
- Hidenori Goto (hidenorigoto)
- Brandon Turner (blt04)
- Marc Weistroff (marcw)
- John Wards (johnwards)
- Arnout Boks (aboks)
- Antoine Hérault (Herzult)
- Brikou CARRE (brikou)
- Alexandre Salomé (alexandresalome)
- Daniel Holmes (danielholmes)
- Fabien Pennequin (FabienPennequin)
- Matthieu Vachon (maoueh)
- Katsuhiro OGAWA (fivestar)
- stealth35 (stealth35)
- Matthieu Bontemps (mbontemps)
- Robert Schönthal (digitalkaoz)
- Michel Weimerskirch (mweimerskirch)
- blue-eyes (blue-eyes)
- Tim Nagel (merk)
- Francois Zaninotto (fzaninotto)
- Richard Shank (IamPersistent)
- Amal Raghav (kertz)
- Konstantin Leboev (realmfoo)
- Jeroen Hoek (jdhoek)
- Christian Raue (craue)
- Arjen Brouwer (arjenjb)
- Jakub Zalas (jakzal)
- Artur Kotyrba (udat)
- Jacob Dreesen (jdreesen)
- Clément JOBEILI (dator)
- Eric Clemmons (ericclemmons)
- Xavier Montaña (xmontana)
- Noel GUILBERT (noelg)
- Martin Schuhfuss (usefulthink)
- Dennis Benkert (denderello)
- Dustin Whittle (dustinwhittle)
- Jean-François PHILIPPE (jfphilippe)
- Justin Hileman (bobthecow)
- dlsniper (dlsniper)
- Sven Paulus (subsven)
- umpirsky (umpirsky)
- Xavier Perez (DuoSRX)
- Brouznouf (Brouznouf)
- Henrik Westphal (snc)
- Boussekeyt Jules (gordonslondon)
- Thomas (rande)
- Pablo Godel (pgodel)
- Leszek Prabucki (l3l0)
- Excel Web Zone (excelwebzone)
- Jan Sorgalla (jsor)
- Lee McDermott (lmcd)
- HIROKI (hirocaster)
- Michel Salib (michelsalib)
- Michael Ridgway (mridgway)
- geoffrey
- Matthew Lewinski (lewinski)
- yktd26 (yktd26)
- heccjj (heccjj)
- Jeremie Augustin
- Aurelijus Valeiša (aurelijus)
- Ray (rrehbeindoi)
- Pierre-Yves LEBECQ (pylebecq)
- Adrian Rudnik (kreischweide)
- Gordon Franke (gimler)
- asm89 (asm89)
- Geoffrey Tran (geoffreytran)
- Alif Rachmawadi (alifity)
- Andréia Bohner (andreia)
- Daniel Gomes (danielcsgomes)
- Shigenobu Nishikawa (shishi)
- Yuen-Chi Lian (yclian)
- 77web (77web)
- Greg Thornton (xdissent)
- frost-nzcr4 (frost-nzcr4)
- Donald Tyler (Chekote)
- Kai
- Gyula Sallai (thesalla)
- Richard Miller (richardmiller)
- Sergey Linnik (Partugal)
- Kevin McBride (krmcbride)
- Laszlo Korte (laszlokorte)
- Derek ROTH (DerekRoth)
- arnaud-lb (arnaud-lb)
- Stepan Tanasiychuk (stfalcon)
- Bertrand Zuchuat (Garfield-fr)
- Jan Behrens (deegital)
- Jan Schumann (janschumann)
- tero (tero)
- Asier Illarramendi (doup)
- Gábor Egyed
- Javier Eguiluz (javiereguiluz)
- Marcin Sikoń (marphi)
- Fabian Lange (CodingFabian)
- Yoshio HANAWA
- Pablo Díez (pablodip)
- Alexander
- Costin Bereveanu (schniper)
- Douglas Greenshields (shieldo)
- Nicolas Badey (Nico-B)
- Benoit Tirmarche (mcbennn)
- De Cock Xavier (xdecock)
- Cristian González Sánchez (cristiangsp)
- Grégoire Passault (Gregwar)
- Nils Adermann (naderman)
- patashnik (patashnik)
- Tobias Naumann (tna)
- Steven Surowiec (steves)
- Baptiste Clavié (Taluu)
- Ivan Rey (ivanrey)
- Ned Schwartz (theinterned)
- Aurélien Fredouelle (AurelC2G)
- Oncle Tom (oncletom)
- Michael Roterman (wtfzdotnet)
- Christian Schaefer (caefer)
- Elliot Anderson (elliot)
- Sortex
- Tobias Sjösten (tobiassjosten)
- José Nahuel Cuesta Luengo (ncuesta)
- erheme318 (erheme318)
- Michael Holm (hollodk)
- Rostyslav Kinash
- umpirski (umpirski)
- Davide Borsatto (davideborsatto)
- Sebastian Bergmann (sebastianbergmann)
- Hossein Bukhamsin (husinluck)
- Laurent Bachelier (laurentb)
- Fabrice Bernhard (fabriceb)
- develop
- Joshua Nye (zerosanity)
- markchalloner (markchalloner)
- Michael Williams (mtotheikle)
- Casper Valdemar Poulsen
- Degory Valentine
- Krzysiek Łabuś (Crozin)
- Lars Strojny
- Jan Prieser (jaypea)
- Christoph Nißle (DerStoffel)
- Guilherme Blanco (guilhermeblanco)
- paulkamer (paulkamer)
- Peter Kruithof (pkruithof)
- Albert Jessurum (ajessu)
- Gerard van Helden (drm)
- Abhoryo (Abhoryo)
- Fabian Vogler (fabian)
- Cyril Quintin (cyqui)
- kazusuke sasezaki (sasezaki)
- Martin Mayer (martinmayer)
- Emil Einarsson (Einarsson)
- mwsaz
- Gabriel Birke (gbirke)
- Osman Üngür (import)
- Matthew Davis (mdavis1982)
- Don Pinkster
- hlecorche
- Vyacheslav Slinko
- Grégoire Passault
- Daniel Cestari (dcestari)
- Vladislav (ideea)
- Tom Klingenberg (ktomk)
- ouardisoft (ouardisoft)
- Pierre Minnieur (pminnieur)
- Benoît Merlet (trompette)
- Thomas Adam (tecbot)
- Florent Cailhol (ooflorent)
- Théophile Helleboid - chtitux (chtitux)
- irmantas (irmantas)
- chispita
- Michele Orselli (micheleorselli)
- Wojciech Sznapka (wowo)
- John Kary (johnkary)
- Martijn Evers (martijn4evers)
- Jeremy Bush (zombor)
- kaiwa
- Gustavo Adrian
- Nicolas Fabre (nfabre)
- Benjamin Dulau (benjamindulau)
- Julien Brochet (aerialls)
- Sebastian Utz (seut)
- George Giannoulopoulos (dotoree)
- Bart00 (Bart00)
- Jay Severson
- meckhardt (meckhardt)
- Joseph Rouff (rouffj)
- Sebastian Ionescu
- Dirk Pahl (dirkaholic)
- Bouke Haarsma (Bouke)
- Oleg Zinchenko (cystbear)
- Benjamin Lévêque (benji07)
- Sebastian Hörl (blogsh)
- Benjamin Zikarsky (bzikarsky)
- Romain Dorgueil (hartym)
- Andy Stanberry (cranberyxl)
- Jérôme Macias (jeromemacias)
- Philip Dahlstrøm (phidah)
- Gustavo Falco
- gnat42 (gnat42)
- Kevin McBride
- devel
- Jan Eichhorn (Exeu)
- Alexander Zogheb
- Dan Ordille (dordille)
- Tuxosaurus (Tuxosaurus)
- Lenar Lõhmus
- Alex
- Klaas Naaijkens
- Masao Maeda (brtriver)
- Gustavo Adrian (comfortablynumb)
- Kévin Dunglas (dunglas)
- Robert Campbell (jayrulez)
- Jimmy Leger (redpanda)
- Tom Van Looy
- Nicolas A. Bérard-Nault
- Alexey Popkov
- Adán Lobato (adanlobato)
- chesteroni (chesteroni)
- Beau Simensen (simensen)
- Juan Ases García (Ases)
- FabienD (FabienD)
- Matt Drollette (MDrollette)
- Skorney (Skorney)
- Sébastien HOUZE
- Sergiy Sokolenko
- Vladimir Sazhin (cannie)
- catchamonkey (catchamonkey)
- Christian Stocker (chregu)
- Luis Cordova (cordoval)
- Damien Alexandre (damienalexandre)
- Damon Jones (damonjones)
- Djama Suemenich (djama)
- Daniel Londero (dlondero)
- dorkitude (dorkitude)
- Kousuke Ebihara (ebihara)
- Abdulkadir N. A. (kadeer)
- Krzysztof Menżyk (krymen)
- kwiateusz (kwiateusz)
- Samuel Laulhau (lalop)
- LAUNAY (laupiFrpar)
- Penny Leach (mjollnir)
- Michael Schneider (mschneid)
- Drew Butler (nodrew)
- Petr Jaroš (petajaros)
- pzwosta (pzwosta)
- Ruud Kamphuis (ruudk)
- Matt Fitzgerald (tirnanog06)
- Josiah (web-dev)
- Gustavo Adrian
- max
- Marcel Beerta (mazen)
- Nicolas de Marqué Fromentin (nicodmf)
- Pierre (ptheg)

View File

@ -1,31 +1,337 @@
プロジェクトをアップデートする方法
==================================
このドキュメントでは、Symfony2 PRの特定のバージョンから1つ次のバージョンへアップデートする方法を説明します。
このドキュメントでは、Symfony2 の特定のバージョンから1つ次のバージョンへアップデートする方法を説明します。
このドキュメントでは、フレームワークの "パブリックな" APIを使っている場合に必要な変更点についてのみ説明しています。
フレームワークのコアコードを "ハック" している場合は、変更履歴を注意深く追跡する必要があるでしょう。
RC4 から RC5
------------
* `MapFileClassLoader` は削除され `MapClassLoader` が採用されました。
* `exception_controller` の設定は、 `framework` セクションの `twig` へ移動しました。
* カスタムエラーページは、現在 `TwigBundle` の代わりに `FrameworkBundle` を参照する必要があります。(参照 http://symfony.com/doc/2.0/cookbook/controller/error_pages.html)
* `EntityUserProvider` クラスは Bridge へ移動されました。
FQCN は `Symfony\Component\Security\Core\User\EntityUserProvider` から
`Symfony\Bridge\Doctrine\Security\User\EntityUserProvider` に変更になります。
* `HeaderBag` からの Cookie アクセスが削除されました。
リクエスト Cookie へのアクセスには、`Request::$cookies` を使ってください。
* `ResponseHeaderBag::getCookie()` メソッドと `ResponseHeaderBag::hasCookie()` メソッドは削除されました。
* `ResponseHeaderBag::getCookies()` メソッドの引数で、戻り値のフォーマットを指定できるようになりました。指定できる値は `ResponseHeaderBag::COOKIES_FLAT` (デフォルト値) または `ResponseHeaderBag::COOKIES_ARRAY` です。
* `ResponseHeaderBag::COOKIES_FLAT` を指定すると、戻り値は単純な配列になります配列のキーは、Cookie の名前ではなくなります):
* array(0 => `Cookie インスタンス`, 1 => `別の Cookie インスタンス`)
* `ResponseHeaderBag::COOKIES_ARRAY` を指定すると、戻り値は多次元配列になります:
* array(`ドメイン` => array(`パス` => array(`Cookie 名` => `Cookie インスタンス`)))
* 制約は有効となったキーのみを保持し、その値は保持していないため、Choice 制約の推測クラスGuesserは削除されました。
* MonologBundle の設定のリファクタリングが行われました。
* プロセッサでサポートされるのは、サービスのみです。このサービスは `monolog.processor` タグを使って登録します。次の 3 つの属性を指定できます:
* `handler`: 特定のハンドラーのみに対して登録する場合、そのハンドラーの名前
* `channel`: 特定のロギングチャンネルのみに対して登録する場合のチャンネル (`handler` とどちらか一方のみを指定)
* `method`: レコードの処理に使用するメソッド (指定しない場合は `__invoke` が使われます)
* `SwiftMailerHandler` の email_prototype 設定に指定できるのは、サービスのみです。
* 変更前:
email_prototype: @acme_demo.monolog.email_prototype
* 変更後:
email_prototype: acme_demo.monolog.email_prototype
もしくは、次のようにしてプロトタイプ用のファクトリを使うこともできます:
email_prototype:
id: acme_demo.monolog.email_prototype
method: getPrototype
* セキュリティを考慮し、プロキシ由来の HTTP ヘッダー (`HTTP_X_FORWARDED_FOR`、`X_FORWARDED_PROTO`、`X_FORWARDED_HOST` 等) は、デフォルトでは信頼されなくなりました。リバースプロキシ経由でアプリケーションを利用する構成の場合は、次のように設定してください:
framework:
trust_proxy_headers: true
* 意図しない名前の衝突を避けるため、AbstractType によるフォームタイプ名の自動定義は行われなくなりました。カスタムタイプを作成する場合は、明示的に `getName()` メソッドを実装する必要があります。
RC3 から RC4
------------
* Annotation クラスには、@Annotation を付加してください。
(例については Validator コンポーネントの制約クラスを参照してください)
* アテーションのオートロードには、PHP の機構ではなく独自の機構が使われるように変更されました。
これにより、失敗の状態についてより制御できるようになりました。
コードを動作させるようにするには、`autoload.php` ファイルの末尾に次のコードを追加してください:
use Doctrine\Common\Annotations\AnnotationRegistry;
AnnotationRegistry::registerLoader(function($class) use ($loader) {
$loader->loadClass($class);
return class_exists($class, false);
});
AnnotationRegistry::registerFile(
__DIR__.'/../vendor/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'
);
`$loader` 変数は `UniversalClassLoader` のインスタンスです。
また、ORM のパスを `DoctrineAnnotations.php` に変更しなければいけない場合もあります。
`UniversalClassLoader` を使っていない場合、アノテーションの登録の詳細については、[Doctrine アノテーションドキュメント](http://www.doctrine-project.org/docs/common/2.1/en/reference/annotations.html) を参照してください。
beta5 から RC1
--------------
* `Symfony\Bundle\FrameworkBundle\Command\Command` クラスの名前が
`Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand` に変更されました。
* ルーティングの `AnnotGlobLoader` クラスが削除されました。
* Twig フォームテンプレートのいくつかのブロックの名前は、衝突を避けるために変更されました。
* `container_attributes` から `widget_container_attributes`
* `attributes` から `widget_attributes`
* `options` から `widget_choice_options`
* イベントの変更:
* すべてのリスナーには、`kernel.listener` タグではなく `kernel.event_listener` タグを設定する必要があります。
* カーネルイベントのプレフィックスが `core` から `kernel` に変更されました:
* 変更前:
<tag name="kernel.listener" event="core.request" method="onCoreRequest" />
* 変更後:
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
Note: メソッド名 method 属性で独立して指定できるので、`onCoreRequest` のままでも動作しますが、将来的な一貫性のためにイベント名に合わせたメソッド名に変更しておく方がよいでしょう。
* `Symfony\Component\HttpKernel\CoreEvents` クラスの名前が
`Symfony\Component\HttpKernel\KernelEvents` に変更されました。
* `TrueValidator``FalseValidator` の受け付ける値をより限定しました。
beta4 から beta5
----------------
* `UserProviderInterface::loadUser()` メソッドの名前は、メソッドの目的がより明確になるよう、`UserProviderInterface::refreshUser()` に変更されました。
* `WebTestCase` クラスの `$kernel` プロパティは static に変更されました。
ファンクショナルテスト内で `$this->kernel` を使っている箇所は、`self::$kernel` に変更してください。
* AsseticBundle は独立したリポジトリで管理されるようになりましたSymfony2 Standard Edition にはバンドルされています)。
* Yaml コンポーネントの変更:
* Exception クラスは独自の名前空間へ移動されました。
* `Yaml::load()` メソッドの名前は `Yaml::parse()` に変更されました。
* `HttpFoundation` コンポーネントの `File` クラスのリファクタリング:
* `Symfony\Component\HttpFoundation\File\File` の API が新しくなりました。
* `\SplFileInfo` を継承するようになりました
* `getName()``getBasename()` に変更
* `getDirectory()``getPath()` に変更
* `getPath()``getRealPath()` に変更
* `move()` メソッドを呼び出した時に、対象ディレクトリがまだ存在していない場合は作成されるようになりました。
* `getExtension()``guessExtension()` の戻り値の拡張子から、先頭の `.` が除去されるように変更されました。
* `Symfony\Component\HttpFoundation\File\UploadedFile` の API が新しくなりました。
* コンストラクタに真偽値の引数が追加されました。
この引数に true を指定すると、ファイルを移動できるようになりますが、テストモード以外では true に設定しないでください。
コアファイル以外の外部から true に設定することは想定していません。
* `getMimeType()` は、対象ファイルの MIME タイプを必ず返すように変更されました。
リクエストから MIME タイプを取得する場合は、`getClientMimeType()` メソッドを使ってください。
* `getSize()` は、対象ファイルのサイズを必ず返すように変更されました。
リクエストからファイルサイズを取得する場合は、`getClientSize()` メソッドを使ってください。
* リクエストからオリジナルのファイル名を取得する場合は、`getClientOriginalName()` メソッドを使ってください。
* Twig の `extensions` 設定は削除されました。
Twig エクステンションを登録する場合は、`twig.extension` タグを使ってください。
* Monolog ハンドラのスタックで、デフォルトで記録が伝播されるようになりました。
伝播されないようにするには、bubble を明示的に false に設定してください。
* `SerializerInterface` が拡張されました。
Serializer クラスのパブリックメソッドの数は減りましたが、後方互換性が損なわれ、コンポーネント独自の Exception クラスが追加されました。
* `FileType` フォームクラスが大きく変更されました。
* テンポラリストレージが削除されました。
* FileType の `type` オプションが削除されました。
新しい動作は、以前の `type``file` を設定した場合の動作と同じです。
* ファイルウィジェットは、他の INPUT フィールドと同じようにレンダリングされるように変更されました。
* Doctrine の `EntityType` クラスコンストラクタの `em` 引数には、EntityManager インスタンスの代わりにエンティティマネージャー名を指定するよう変更されました。
このオプションをを渡さない場合、以前と同じようにデフォルトのエンティティマネージャーが使われます。
* Console コンポーネントの中の `Command::getFullname()` メソッドと `Command::getNamespace()` メソッドは削除されました
(`Command::getName()` メソッドの振る舞いは以前の `Command::getFullname()` メソッドと同じになりました)。
* デフォルトの Twig フォームテンプレートは Twig bridge に移動されました。以下のようにすればテンプレートや
コンフィギュレーション設定中で現在Twig フォームテンプレートを参照できます:
変更前:
TwigBundle:Form:div_layout.html.twig
変更後:
form_div_layout.html.twig
* キャッシュウォーマーに関連する設定は、すべて削除されました。
* `Response::isRedirected()` メソッドは `Response::isRedirect()` メソッドに統合されました。
beta3 から beta4
----------------
* `Profile` のインスタンスを返す `Client::getProfile()` メソッドへの変更に従い、`Client::getProfiler()` メソッドは削除されました。
* いくつかの `UniversalClassLoader` のメソッド名は変更されました:
* `registerPrefixFallback` から `registerPrefixFallbacks`
* `registerNamespaceFallback` から `registerNamespaceFallbacks`
* イベントシステムはさらに柔軟になりました。リスナーは任意の有効でコール可能な PHP 関数であれば可能になりました。
* `EventDispatcher::addListener($eventName, $listener, $priority = 0)`:
* `$eventName` がイベント名で (もう配列ではいけません)、
* `$listener` が コール可能な PHP 関数です。
* イベントクラス名と定数が変更されました:
* 以前の `Symfony\Component\Form\Events` のクラス名と定数:
Events::preBind = 'preBind'
Events::postBind = 'postBind'
Events::preSetData = 'preSetData'
Events::postSetData = 'postSetData'
Events::onBindClientData = 'onBindClientData'
Events::onBindNormData = 'onBindNormData'
Events::onSetData = 'onSetData'
* 新しい `Symfony\Component\Form\FormEvents` クラス名と定数:
FormEvents::PRE_BIND = 'form.pre_bind'
FormEvents::POST_BIND = 'form.post_bind'
FormEvents::PRE_SET_DATA = 'form.pre_set_data'
FormEvents::POST_SET_DATA = 'form.post_set_data'
FormEvents::BIND_CLIENT_DATA = 'form.bind_client_data'
FormEvents::BIND_NORM_DATA = 'form.bind_norm_data'
FormEvents::SET_DATA = 'form.set_data'
* 以前の `Symfony\Component\HttpKernel\Events` のクラス名と定数:
Events::onCoreRequest = 'onCoreRequest'
Events::onCoreException = 'onCoreException'
Events::onCoreView = 'onCoreView'
Events::onCoreController = 'onCoreController'
Events::onCoreResponse = 'onCoreResponse'
* 新しい `Symfony\Component\HttpKernel\CoreEvents` のクラス名と定数:
CoreEvents::REQUEST = 'core.request'
CoreEvents::EXCEPTION = 'core.exception'
CoreEvents::VIEW = 'core.view'
CoreEvents::CONTROLLER = 'core.controller'
CoreEvents::RESPONSE = 'core.response'
* 以前の `Symfony\Component\Security\Http\Events` のクラス名と定数:
Events::onSecurityInteractiveLogin = 'onSecurityInteractiveLogin'
Events::onSecuritySwitchUser = 'onSecuritySwitchUser'
* 新しい `Symfony\Component\Security\Http\SecurityEvents` のクラス名と定数:
SecurityEvents::INTERACTIVE_LOGIN = 'security.interactive_login'
SecurityEvents::SWITCH_USER = 'security.switch_user'
* `addListenerService` は第 1 引数として単一のイベント名だけを取るようになりました。
* コンフィギュレーションのタグでは、呼び出すメソッドを指定する必要があります。
* 変更前:
<tag name="kernel.listener" event="onCoreRequest" />
* 変更後:
<tag name="kernel.listener" event="core.request" method="onCoreRequest" />
* Subscriber は常に連想配列を返すようになりました:
* 変更前:
public static function getSubscribedEvents()
{
return Events::onBindNormData;
}
* 変更後:
public static function getSubscribedEvents()
{
return array(FormEvents::BIND_NORM_DATA => 'onBindNormData');
}
* フォーム `DateType` パラメーターの `single-text``single_text` へ変更されました
* フォームフィールドラベルヘルパーは属性の設定も受け入れるようになりました。例 :
```html+jinja
{{ form_label(form.name, 'Custom label', { 'attr': {'class': 'name_field'} }) }}
```
* Swiftmailer を使うためには、autoloader ("app/autoloader.php") を通して "init.php" を登録し、
`Swift_` prefix の登録を autoloader から削除しなければなりません。これをどのように行うべきかの例は、
Standard Distribution をご覧ください。
[autoload.php](https://github.com/symfony/symfony-standard/blob/v2.0.0BETA4/app/autoload.php#L29).
beta2 から beta3
----------------
* `framework.annotations` に属する設定が少し変更されました。
変更前:
変更前:
framework:
annotations:
cache: file
file_cache:
framework:
annotations:
cache: file
file_cache:
debug: true
dir: /foo
変更後:
framework:
annotations:
cache: file
debug: true
dir: /foo
変更後:
framework:
annotations:
cache: file
debug: true
file_cache_dir: /foo
file_cache_dir: /foo
beta1 から beta2
----------------

386
UPDATE.md
View File

@ -1,37 +1,379 @@
How to update your project?
===========================
This document explains how to upgrade from one Symfony2 PR version to the next
This document explains how to upgrade from one Symfony2 version to the next
one. It only discusses changes that need to be done when using the "public"
API of the framework. If you "hack" the core, you should probably follow the
timeline closely anyway.
RC4 to RC5
----------
* The `MapFileClassLoader` has been removed in favor of a new
`MapClassLoader`.
* The `exception_controller` setting has been moved from the `framework`
section to the `twig` one.
* The custom error pages must now reference `TwigBundle` instead of
`FrameworkBundle` (see
http://symfony.com/doc/2.0/cookbook/controller/error_pages.html)
* `EntityUserProvider` class has been moved and FQCN changed from
`Symfony\Component\Security\Core\User\EntityUserProvider` to
`Symfony\Bridge\Doctrine\Security\User\EntityUserProvider`.
* Cookies access from `HeaderBag` has been removed. Accessing Request cookies
must be done via `Request::$cookies``.
* `ResponseHeaderBag::getCookie()` and `ResponseHeaderBag::hasCookie()`
methods were removed.
* The method `ResponseHeaderBag::getCookies()` now supports an argument for the
returned format (possible values are `ResponseHeaderBag::COOKIES_FLAT`
(default value) or `ResponseHeaderBag::COOKIES_ARRAY`).
* `ResponseHeaderBag::COOKIES_FLAT` returns a simple array (the array keys
are not cookie names anymore):
* array(0 => `a Cookie instance`, 1 => `another Cookie instance`)
* `ResponseHeaderBag::COOKIES_ARRAY` returns a multi-dimensional array:
* array(`the domain` => array(`the path` => array(`the cookie name` => `a Cookie instance`)))
* Removed the guesser for the Choice constraint as the constraint only knows
about the valid keys, and not their values.
* The configuration of MonologBundle has been refactored.
* Only services are supported for the processors. They are now registered
using the `monolog.processor` tag which accept three optionnal attributes:
* `handler`: the name of an handler to register it only for a specific handler
* `channel`: to register it only for one logging channel (exclusive with `handler`)
* `method`: The method used to process the record (`__invoke` is used if not set)
* The email_prototype for the `SwiftMailerHandler` only accept a service id now.
* Before:
email_prototype: @acme_demo.monolog.email_prototype
* After:
email_prototype: acme_demo.monolog.email_prototype
or if you want to use a factory for the prototype:
email_prototype:
id: acme_demo.monolog.email_prototype
method: getPrototype
* To avoid security issues, HTTP headers coming from proxies are not trusted
anymore by default (like `HTTP_X_FORWARDED_FOR`, `X_FORWARDED_PROTO`, and
`X_FORWARDED_HOST`). If your application is behind a reverse proxy, add the
following configuration:
framework:
trust_proxy_headers: true
* To avoid hidden naming collisions, the AbstractType does not try to define
the name of form types magically. You now need to implement the `getName()`
method explicitly when creating a custom type.
* Renamed some methods to follow the naming conventions:
Session::getAttributes() -> Session::all()
Session::setAttributes() -> Session::replace()
* {_locale} is not supported in paths in the access_control section anymore. You can
rewrite the paths using a regular expression such as "(?:[a-z]{2})".
RC3 to RC4
----------
* Annotation classes must be annotated with @Annotation
(see the validator constraints for examples)
* Annotations are not using the PHP autoloading but their own mechanism. This
allows much more control about possible failure states. To make your code
work, add the following lines at the end of your `autoload.php` file:
use Doctrine\Common\Annotations\AnnotationRegistry;
AnnotationRegistry::registerLoader(function($class) use ($loader) {
$loader->loadClass($class);
return class_exists($class, false);
});
AnnotationRegistry::registerFile(
__DIR__.'/../vendor/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'
);
The `$loader` variable is an instance of `UniversalClassLoader`.
Additionally you might have to adjust the ORM path to the
`DoctrineAnnotations.php`. If you are not using the `UniversalClassLoader`
see the [Doctrine Annotations
documentation](http://www.doctrine-project.org/docs/common/2.1/en/reference/annotations.html)
for more details on how to register annotations.
beta5 to RC1
------------
* Renamed `Symfony\Bundle\FrameworkBundle\Command\Command` to
`Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand`
* Removed the routing `AnnotGlobLoader` class
* Some blocks in the Twig Form templates have been renamed to avoid
collisions:
* `container_attributes` to `widget_container_attributes`
* `attributes` to `widget_attributes`
* `options` to `widget_choice_options`
* Event changes:
* All listeners must now be tagged with `kernel.event_listener` instead of
`kernel.listener`.
* Kernel events are now properly prefixed with `kernel` instead of `core`:
* Before:
<tag name="kernel.listener" event="core.request" method="onCoreRequest" />
* After:
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
Note: the method can of course remain as `onCoreRequest`, but renaming it
as well for consistency with future projects makes sense.
* The `Symfony\Component\HttpKernel\CoreEvents` class has been renamed to
`Symfony\Component\HttpKernel\KernelEvents`
* `TrueValidator` and `FalseValidator` constraints validators no longer accepts any value as valid data.
beta4 to beta5
--------------
* `UserProviderInterface::loadUser()` has been renamed to
`UserProviderInterface::refreshUser()` to make the goal of the method
clearer.
* The `$kernel` property on `WebTestCase` is now static. Change any instances
of `$this->kernel` in your functional tests to `self::$kernel`.
* The AsseticBundle has been moved to its own repository (it still bundled
with Symfony SE).
* Yaml Component:
* Exception classes have been moved to their own namespace
* `Yaml::load()` has been renamed to `Yaml::parse()`
* The File classes from `HttpFoundation` have been refactored:
* `Symfony\Component\HttpFoundation\File\File` has a new API:
* It now extends `\SplFileInfo`:
* former `getName()` equivalent is `getBasename()`,
* former `getDirectory()` equivalent is `getPath()`,
* former `getPath()` equivalent is `getRealPath()`.
* the `move()` method now creates the target directory when it does not exist,
* `getExtension()` and `guessExtension()` do not return the extension
with a leading `.` anymore
* `Symfony\Component\HttpFoundation\File\UploadedFile` has a new API:
* The constructor has a new Boolean parameter that must be set to true
in test mode only in order to be able to move the file. This parameter
is not intended to be set to true from outside of the core files.
* `getMimeType()` now always returns the mime type of the underlying file.
Use `getClientMimeType()` to get the mime type from the request.
* `getSize()` now always returns the size of the underlying file.
Use `getClientSize()` to get the file size from the request.
* Use `getClientOriginalName()` to retrieve the original name from the
request.
* The `extensions` setting for Twig has been removed. There is now only one
way to register Twig extensions, via the `twig.extension` tag.
* The stack of Monolog handlers now bubbles the records by default. To stop
the propagation you need to configure the bubbling explicitly.
* Expanded the `SerializerInterface`, while reducing the number of public
methods in the Serializer class itself breaking BC and adding component
specific Exception classes.
* The `FileType` Form class has been heavily changed:
* The temporary storage has been removed.
* The file type `type` option has also been removed (the new behavior is
the same as when the `type` was set to `file` before).
* The file input is now rendered as any other input field.
* The `em` option of the Doctrine `EntityType` class now takes the entity
manager name instead of the EntityManager instance. If you don't pass this
option, the default Entity Manager will be used as before.
* In the Console component: `Command::getFullname()` and
`Command::getNamespace()` have been removed (`Command::getName()` behavior
is now the same as the old `Command::getFullname()`).
* Default Twig form templates have been moved to the Twig bridge. Here is how
you can reference them now from a template or in a configuration setting:
Before:
TwigBundle:Form:div_layout.html.twig
After:
form_div_layout.html.twig
* All settings regarding the cache warmers have been removed.
* `Response::isRedirected()` has been merged with `Response::isRedirect()`
beta3 to beta4
--------------
* Form DateType parameter single-text changed to single_text
* `Client::getProfiler` has been removed in favor of `Client::getProfile`,
which returns an instance of `Profile`.
* Some `UniversalClassLoader` methods have been renamed:
* `registerPrefixFallback` to `registerPrefixFallbacks`
* `registerNamespaceFallback` to `registerNamespaceFallbacks`
* The event system has been made more flexible. A listener can now be any
valid PHP callable.
* `EventDispatcher::addListener($eventName, $listener, $priority = 0)`:
* `$eventName` is the event name (cannot be an array anymore),
* `$listener` is a PHP callable.
* The events classes and constants have been renamed:
* Old class name `Symfony\Component\Form\Events` and constants:
Events::preBind = 'preBind'
Events::postBind = 'postBind'
Events::preSetData = 'preSetData'
Events::postSetData = 'postSetData'
Events::onBindClientData = 'onBindClientData'
Events::onBindNormData = 'onBindNormData'
Events::onSetData = 'onSetData'
* New class name `Symfony\Component\Form\FormEvents` and constants:
FormEvents::PRE_BIND = 'form.pre_bind'
FormEvents::POST_BIND = 'form.post_bind'
FormEvents::PRE_SET_DATA = 'form.pre_set_data'
FormEvents::POST_SET_DATA = 'form.post_set_data'
FormEvents::BIND_CLIENT_DATA = 'form.bind_client_data'
FormEvents::BIND_NORM_DATA = 'form.bind_norm_data'
FormEvents::SET_DATA = 'form.set_data'
* Old class name `Symfony\Component\HttpKernel\Events` and constants:
Events::onCoreRequest = 'onCoreRequest'
Events::onCoreException = 'onCoreException'
Events::onCoreView = 'onCoreView'
Events::onCoreController = 'onCoreController'
Events::onCoreResponse = 'onCoreResponse'
* New class name `Symfony\Component\HttpKernel\CoreEvents` and constants:
CoreEvents::REQUEST = 'core.request'
CoreEvents::EXCEPTION = 'core.exception'
CoreEvents::VIEW = 'core.view'
CoreEvents::CONTROLLER = 'core.controller'
CoreEvents::RESPONSE = 'core.response'
* Old class name `Symfony\Component\Security\Http\Events` and constants:
Events::onSecurityInteractiveLogin = 'onSecurityInteractiveLogin'
Events::onSecuritySwitchUser = 'onSecuritySwitchUser'
* New class name `Symfony\Component\Security\Http\SecurityEvents` and constants:
SecurityEvents::INTERACTIVE_LOGIN = 'security.interactive_login'
SecurityEvents::SWITCH_USER = 'security.switch_user'
* `addListenerService` now only takes a single event name as its first
argument,
* Tags in configuration must now set the method to call:
* Before:
<tag name="kernel.listener" event="onCoreRequest" />
* After:
<tag name="kernel.listener" event="core.request" method="onCoreRequest" />
* Subscribers must now always return a hash:
* Before:
public static function getSubscribedEvents()
{
return Events::onBindNormData;
}
* After:
public static function getSubscribedEvents()
{
return array(FormEvents::BIND_NORM_DATA => 'onBindNormData');
}
* Form `DateType` parameter `single-text` changed to `single_text`
* Form field label helpers now accepts setting attributes, i.e.:
```html+jinja
{{ form_label(form.name, 'Custom label', { 'attr': {'class': 'name_field'} }) }}
```
* In order to use Swiftmailer, you should now register its "init.php" file via
the autoloader ("app/autoloader.php") and remove the `Swift_` prefix from
the autoloader. For an example on how this should be done, see the Standard
Distribution
[autoload.php](https://github.com/symfony/symfony-standard/blob/v2.0.0BETA4/app/autoload.php#L29).
beta2 to beta3
--------------
* The settings under "framework.annotations" have changed slightly:
* The settings under `framework.annotations` have changed slightly:
Before:
framework:
annotations:
cache: file
file_cache:
Before:
framework:
annotations:
cache: file
file_cache:
debug: true
dir: /foo
After:
framework:
annotations:
cache: file
debug: true
dir: /foo
After:
framework:
annotations:
cache: file
debug: true
file_cache_dir: /foo
file_cache_dir: /foo
beta1 to beta2
--------------
@ -137,7 +479,7 @@ class AcmeEntity
}
```
* The config under `framework.validation.annotations` has been removed and was
* The config under `framework.validation.annotations` has been removed and was
replaced with a boolean flag `framework.validation.enable_annotations` which
defaults to false.
@ -284,7 +626,7 @@ class AcmeEntity
'allow_add' => true,
'allow_delete' => true,
));
* `Request::hasSession()` has been renamed to `Request::hasPreviousSession()`. The
method `hasSession()` still exists, but only checks if the request contains a
session object, not if the session was started in a previous request.
@ -300,10 +642,10 @@ class AcmeEntity
* Serializer: The `$properties` argument has been dropped from all interfaces.
* Form: Renamed option value `text` of `widget` option of the `date` type was
* Form: Renamed option value `text` of `widget` option of the `date` type was
renamed to `single-text`. `text` indicates to use separate text boxes now
(like for the `time` type).
* Form: Renamed view variable `name` to `full_name`. The variable `name` now
contains the local, short name (equivalent to `$form->getName()`).

View File

@ -3,6 +3,7 @@
require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
use Symfony\Component\ClassLoader\UniversalClassLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
@ -11,14 +12,25 @@ $loader->registerNamespaces(array(
'Doctrine\\Common' => __DIR__.'/vendor/doctrine-common/lib',
'Doctrine\\DBAL' => __DIR__.'/vendor/doctrine-dbal/lib',
'Doctrine' => __DIR__.'/vendor/doctrine/lib',
'Assetic' => __DIR__.'/vendor/assetic/src',
'Monolog' => __DIR__.'/vendor/monolog/src',
));
$loader->registerPrefixes(array(
'Swift_' => __DIR__.'/vendor/swiftmailer/lib/classes',
'Twig_' => __DIR__.'/vendor/twig/lib',
'Twig_' => __DIR__.'/vendor/twig/lib',
));
if (!function_exists('intl_get_error_code')) {
require_once __DIR__.'/src/Symfony/Component/Locale/Resources/stubs/functions.php';
$loader->registerPrefixFallbacks(array(__DIR__.'/src/Symfony/Component/Locale/Resources/stubs'));
}
$loader->register();
$loader->registerPrefixFallback(array(
__DIR__.'/../vendor/symfony/src/Symfony/Component/Locale/Resources/stubs',
));
AnnotationRegistry::registerLoader(function($class) use ($loader) {
$loader->loadClass($class);
return class_exists($class, false);
});
AnnotationRegistry::registerFile(__DIR__.'/vendor/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
if (is_file(__DIR__.'/vendor/swiftmailer/lib/classes/Swift.php')) {
require_once __DIR__.'/vendor/swiftmailer/lib/classes/Swift.php';
Swift::registerAutoload(__DIR__.'/vendor/swiftmailer/lib/swift_init.php');
}

106
check_cs Executable file
View File

@ -0,0 +1,106 @@
#!/usr/bin/env php
<?php
/*
* Coding Standards (a.k.a. CS)
*
* This script is designed to clean up the source files and thus follow coding
* conventions.
*
* @see http://symfony.com/doc/2.0/contributing/code/standards.html
*
*/
require_once __DIR__.'/autoload.php.dist';
use Symfony\Component\Finder\Finder;
$fix = isset($argv[1]) && 'fix' == $argv[1];
$finder = new Finder();
$finder
->files()
->name('*.md')
->name('*.php')
->name('*.php.dist')
->name('*.twig')
->name('*.xml')
->name('*.xml.dist')
->name('*.yml')
->in(array(__DIR__.'/src', __DIR__.'/tests'))
->notName(basename(__FILE__))
->exclude('.git')
->exclude('vendor')
;
$count = 0;
foreach ($finder as $file) {
/* @var $file Symfony\Component\Finder\SplFileInfo */
// These files are skipped because tests would break
foreach(array(
'tests/Symfony/Tests/Component/ClassLoader/ClassCollectionLoaderTest.php',
'tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/container9.php',
'tests/Symfony/Tests/Component/DependencyInjection/Fixtures/includes/foo.php',
'tests/Symfony/Tests/Component/DependencyInjection/Fixtures/php/services9.php',
'tests/Symfony/Tests/Component/DependencyInjection/Fixtures/yaml/services9.yml',
'tests/Symfony/Tests/Component/Routing/Fixtures/dumper/url_matcher1.php',
'tests/Symfony/Tests/Component/Routing/Fixtures/dumper/url_matcher2.php',
'tests/Symfony/Tests/Component/Yaml/Fixtures/sfTests.yml',
) as $skippedFile) {
if ($skippedFile === substr($file->getRealPath(), strlen($skippedFile) * -1)) {
continue(2);
}
}
$old = file_get_contents($file->getRealpath());
$new = $old;
// [Structure] Never use short tags (<?)
$new = str_replace('<? ', '<?php ', $new);
// [Structure] Indentation is done by steps of four spaces (tabs are never allowed)
$new = preg_replace_callback('/^( *)(\t+)/m', function ($matches) use ($new) {
return $matches[1].str_repeat(' ', strlen($matches[2]));
}, $new);
// [Structure] Use the linefeed character (0x0A) to end lines
$new = str_replace("\r\n", "\n", $new);
// [Structure] Don't add trailing spaces at the end of lines
$new = preg_replace('/[ \t]*$/m', '', $new);
// [Structure] Add a blank line before return statements
$new = preg_replace_callback('/(^.*$)\n(^ +return)/m', function ($match) {
// don't add it if the previous line is ...
if (
preg_match('/\{$/m', $match[1]) || // ... ending with an opening brace
preg_match('/\:$/m', $match[1]) || // ... ending with a colon (e.g. a case statement)
preg_match('%^ *//%m', $match[1]) || // ... an inline comment
preg_match('/^$/m', $match[1]) // ... already blank
) {
return $match[1]."\n".$match[2];
}
return $match[1]."\n\n".$match[2];
}, $new);
// [Structure] A file must always ends with a linefeed character
if (strlen($new) && "\n" != substr($new, -1)) {
$new .= "\n";
}
if ($new != $old) {
$count++;
if ($fix) {
file_put_contents($file->getRealpath(), $new);
}
printf('%4d) %s'.PHP_EOL, $count, $file->getRelativePathname());
}
}
exit($count ? 1 : 0);

View File

@ -1,6 +1,15 @@
<?php
namespace Symfony\Bundle\DoctrineBundle\Annotations;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bridge\Doctrine\Annotations;
use Doctrine\Common\Annotations\Reader;
@ -64,4 +73,4 @@ class IndexedReader implements Reader
{
return $this->delegate->getPropertyAnnotation($property, $annotation);
}
}
}

View File

@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Doctrine\CacheWarmer;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
/**
* The proxy generator cache warmer generates all entity proxies.
*
* In the process of generating proxies the cache for all the metadata is primed also,
* since this information is necessary to build the proxies in the first place.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class ProxyCacheWarmer implements CacheWarmerInterface
{
private $registry;
/**
* Constructor.
*
* @param RegistryInterface $registry A RegistryInterface instance
*/
public function __construct(RegistryInterface $registry)
{
$this->registry = $registry;
}
/**
* This cache warmer is not optional, without proxies fatal error occurs!
*
* @return false
*/
public function isOptional()
{
return false;
}
public function warmUp($cacheDir)
{
foreach ($this->registry->getEntityManagers() as $em) {
// we need the directory no matter the proxy cache generation strategy
if (!is_dir($proxyCacheDir = $em->getConfiguration()->getProxyDir())) {
if (false === @mkdir($proxyCacheDir, 0777, true)) {
throw new \RuntimeException(sprintf('Unable to create the Doctrine Proxy directory "%s".', dirname($proxyCacheDir)));
}
} elseif (!is_writable($proxyCacheDir)) {
throw new \RuntimeException(sprintf('The Doctrine Proxy directory "%s" is not writeable for the current system user.', $proxyCacheDir));
}
// if proxies are autogenerated we don't need to generate them in the cache warmer
if ($em->getConfiguration()->getAutoGenerateProxyClasses()) {
continue;
}
$classes = $em->getMetadataFactory()->getAllMetadata();
$em->getProxyFactory()->generateProxyClasses($classes);
}
}
}

View File

@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\DoctrineBundle\DataCollector;
namespace Symfony\Bridge\Doctrine\DataCollector;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\DoctrineBundle\Logger\DbalLogger;
use Symfony\Bundle\DoctrineBundle\Registry;
use Symfony\Bridge\Doctrine\Logger\DbalLogger;
use Symfony\Bridge\Doctrine\RegistryInterface;
/**
* DoctrineDataCollector.
@ -28,7 +28,7 @@ class DoctrineDataCollector extends DataCollector
private $managers;
private $logger;
public function __construct(Registry $registry, DbalLogger $logger = null)
public function __construct(RegistryInterface $registry, DbalLogger $logger = null)
{
$this->connections = $registry->getConnectionNames();
$this->managers = $registry->getEntityManagerNames();

View File

@ -169,6 +169,10 @@ class EntityChoiceList extends ArrayChoiceList
$value = $this->propertyPath->getValue($entity);
} else {
// Otherwise expect a __toString() method in the entity
if (!method_exists($entity, '__toString')) {
throw new FormException('Entities passed to the choice field must have a "__toString()" method defined (or you can also override the "property" option).');
}
$value = (string)$entity;
}
@ -181,7 +185,7 @@ class EntityChoiceList extends ArrayChoiceList
// entity ID for performance reasons
$id = current($this->getIdentifierValues($entity));
}
if (null === $group) {
// Flat list of choices
$this->choices[$id] = $value;
@ -189,7 +193,7 @@ class EntityChoiceList extends ArrayChoiceList
// Nested choices
$this->choices[$group][$id] = $value;
}
$this->entities[$id] = $entity;
}
}
@ -221,7 +225,7 @@ class EntityChoiceList extends ArrayChoiceList
* Returns the entity for the given key
*
* If the underlying entities have composite identifiers, the choices
* are intialized. The key is expected to be the index in the choices
* are initialized. The key is expected to be the index in the choices
* array in this case.
*
* If they have single identifiers, they are either fetched from the
@ -242,6 +246,7 @@ class EntityChoiceList extends ArrayChoiceList
if (count($this->identifier) > 1) {
// $key is a collection index
$entities = $this->getEntities();
return isset($entities[$key]) ? $entities[$key] : null;
} else if ($this->entities) {
return isset($this->entities[$key]) ? $this->entities[$key] : null;
@ -295,4 +300,4 @@ class EntityChoiceList extends ArrayChoiceList
return $this->unitOfWork->getEntityIdentifier($entity);
}
}
}

View File

@ -41,7 +41,7 @@ class EntitiesToArrayTransformer implements DataTransformerInterface
}
if (!($collection instanceof Collection)) {
throw new UnexpectedTypeException($collection, 'Doctrine\Common\Collection\Collection');
throw new UnexpectedTypeException($collection, 'Doctrine\Common\Collections\Collection');
}
$array = array();
@ -57,7 +57,8 @@ class EntitiesToArrayTransformer implements DataTransformerInterface
}
} else {
foreach ($collection as $entity) {
$array[] = current($this->choiceList->getIdentifierValues($entity));
$value = current($this->choiceList->getIdentifierValues($entity));
$array[] = is_numeric($value) ? (int) $value : $value;
}
}
@ -100,4 +101,4 @@ class EntitiesToArrayTransformer implements DataTransformerInterface
return $collection;
}
}
}

View File

@ -75,4 +75,4 @@ class EntityToIdTransformer implements DataTransformerInterface
return $entity;
}
}
}

View File

@ -12,30 +12,26 @@
namespace Symfony\Bridge\Doctrine\Form;
use Symfony\Component\Form\AbstractExtension;
use Doctrine\ORM\EntityManager;
use Symfony\Bridge\Doctrine\RegistryInterface;
class DoctrineOrmExtension extends AbstractExtension
{
/**
* The Doctrine 2 entity manager
* @var Doctrine\ORM\EntityManager
*/
protected $em = null;
protected $registry;
public function __construct(EntityManager $em)
public function __construct(RegistryInterface $registry)
{
$this->em = $em;
$this->registry = $registry;
}
protected function loadTypes()
{
return array(
new Type\EntityType($this->em),
new Type\EntityType($this->registry),
);
}
protected function loadTypeGuesser()
{
return new DoctrineOrmTypeGuesser($this->em);
return new DoctrineOrmTypeGuesser($this->registry);
}
}

View File

@ -15,29 +15,19 @@ use Symfony\Component\Form\FormTypeGuesserInterface;
use Symfony\Component\Form\Guess\Guess;
use Symfony\Component\Form\Guess\TypeGuess;
use Symfony\Component\Form\Guess\ValueGuess;
use Doctrine\ORM\EntityManager;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Doctrine\ORM\Mapping\MappingException;
class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface
{
/**
* The Doctrine 2 entity manager
* @var Doctrine\ORM\EntityManager
*/
protected $em = null;
protected $registry;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
private $cache;
/**
* Returns whether Doctrine 2 metadata exists for that class
*
* @return Boolean
*/
protected function isMappedClass($class)
public function __construct(RegistryInterface $registry)
{
return !$this->em->getConfiguration()->getMetadataDriverImpl()->isTransient($class);
$this->registry = $registry;
$this->cache = array();
}
/**
@ -45,94 +35,47 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface
*/
public function guessType($class, $property)
{
if ($this->isMappedClass($class)) {
$metadata = $this->em->getClassMetadata($class);
if ($metadata->hasAssociation($property)) {
$multiple = $metadata->isCollectionValuedAssociation($property);
$mapping = $metadata->getAssociationMapping($property);
return new TypeGuess(
'entity',
array(
'em' => $this->em,
'class' => $mapping['targetEntity'],
'multiple' => $multiple,
),
Guess::HIGH_CONFIDENCE
);
} else {
switch ($metadata->getTypeOfField($property))
{
// case 'array':
// return new TypeGuess(
// 'Collection',
// array(),
// Guess::HIGH_CONFIDENCE
// );
case 'boolean':
return new TypeGuess(
'checkbox',
array(),
Guess::HIGH_CONFIDENCE
);
case 'datetime':
case 'vardatetime':
case 'datetimetz':
return new TypeGuess(
'datetime',
array(),
Guess::HIGH_CONFIDENCE
);
case 'date':
return new TypeGuess(
'date',
array(),
Guess::HIGH_CONFIDENCE
);
case 'decimal':
case 'float':
return new TypeGuess(
'number',
array(),
Guess::MEDIUM_CONFIDENCE
);
case 'integer':
case 'bigint':
case 'smallint':
return new TypeGuess(
'integer',
array(),
Guess::MEDIUM_CONFIDENCE
);
case 'string':
return new TypeGuess(
'text',
array(),
Guess::MEDIUM_CONFIDENCE
);
case 'text':
return new TypeGuess(
'textarea',
array(),
Guess::MEDIUM_CONFIDENCE
);
case 'time':
return new TypeGuess(
'time',
array(),
Guess::HIGH_CONFIDENCE
);
// case 'object': ???
}
}
if (!$ret = $this->getMetadata($class)) {
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
}
return new TypeGuess(
'text',
array(),
Guess::LOW_CONFIDENCE
);
list($metadata, $name) = $ret;
if ($metadata->hasAssociation($property)) {
$multiple = $metadata->isCollectionValuedAssociation($property);
$mapping = $metadata->getAssociationMapping($property);
return new TypeGuess('entity', array('em' => $name, 'class' => $mapping['targetEntity'], 'multiple' => $multiple), Guess::HIGH_CONFIDENCE);
}
switch ($metadata->getTypeOfField($property))
{
//case 'array':
// return new TypeGuess('Collection', array(), Guess::HIGH_CONFIDENCE);
case 'boolean':
return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE);
case 'datetime':
case 'vardatetime':
case 'datetimetz':
return new TypeGuess('datetime', array(), Guess::HIGH_CONFIDENCE);
case 'date':
return new TypeGuess('date', array(), Guess::HIGH_CONFIDENCE);
case 'decimal':
case 'float':
return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE);
case 'integer':
case 'bigint':
case 'smallint':
return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE);
case 'string':
return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE);
case 'text':
return new TypeGuess('textarea', array(), Guess::MEDIUM_CONFIDENCE);
case 'time':
return new TypeGuess('time', array(), Guess::HIGH_CONFIDENCE);
default:
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
}
}
/**
@ -140,22 +83,13 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface
*/
public function guessRequired($class, $property)
{
if ($this->isMappedClass($class)) {
$metadata = $this->em->getClassMetadata($class);
if ($metadata->hasField($property)) {
if (!$metadata->isNullable($property)) {
return new ValueGuess(
true,
Guess::HIGH_CONFIDENCE
);
}
return new ValueGuess(
false,
Guess::MEDIUM_CONFIDENCE
);
$ret = $this->getMetadata($class);
if ($ret && $ret[0]->hasField($property)) {
if (!$ret[0]->isNullable($property)) {
return new ValueGuess(true, Guess::HIGH_CONFIDENCE);
}
return new ValueGuess(false, Guess::MEDIUM_CONFIDENCE);
}
}
@ -164,19 +98,12 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface
*/
public function guessMaxLength($class, $property)
{
if ($this->isMappedClass($class)) {
$metadata = $this->em->getClassMetadata($class);
$ret = $this->getMetadata($class);
if ($ret && $ret[0]->hasField($property) && !$ret[0]->hasAssociation($property)) {
$mapping = $ret[0]->getFieldMapping($property);
if (!$metadata->hasAssociation($property)) {
$mapping = $metadata->getFieldMapping($property);
if (isset($mapping['length'])) {
return new ValueGuess(
$mapping['length'],
Guess::HIGH_CONFIDENCE
);
}
if (isset($mapping['length'])) {
return new ValueGuess($mapping['length'], Guess::HIGH_CONFIDENCE);
}
}
}
@ -186,6 +113,21 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface
*/
public function guessMinLength($class, $property)
{
return;
}
protected function getMetadata($class)
{
if (array_key_exists($class, $this->cache)) {
return $this->cache[$class];
}
$this->cache[$class] = null;
foreach ($this->registry->getEntityManagers() as $name => $em) {
try {
return $this->cache[$class] = array($em->getClassMetadata($class), $name);
} catch (MappingException $e) {
// not an entity or mapped super class
}
}
}
}

View File

@ -11,7 +11,7 @@
namespace Symfony\Bridge\Doctrine\Form\EventListener;
use Symfony\Component\Form\Events;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Event\FilterDataEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@ -25,9 +25,9 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
*/
class MergeCollectionListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
static public function getSubscribedEvents()
{
return Events::onBindNormData;
return array(FormEvents::BIND_NORM_DATA => 'onBindNormData');
}
public function onBindNormData(FilterDataEvent $event)
@ -56,4 +56,4 @@ class MergeCollectionListener implements EventSubscriberInterface
$event->setData($collection);
}
}
}

View File

@ -13,20 +13,20 @@ namespace Symfony\Bridge\Doctrine\Form\Type;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList;
use Symfony\Bridge\Doctrine\Form\EventListener\MergeCollectionListener;
use Symfony\Bridge\Doctrine\Form\DataTransformer\EntitiesToArrayTransformer;
use Symfony\Bridge\Doctrine\Form\DataTransformer\EntityToIdTransformer;
use Symfony\Component\Form\AbstractType;
use Doctrine\ORM\EntityManager;
class EntityType extends AbstractType
{
private $em;
protected $registry;
public function __construct(EntityManager $em)
public function __construct(RegistryInterface $registry)
{
$this->em = $em;
$this->registry = $registry;
}
public function buildForm(FormBuilder $builder, array $options)
@ -44,23 +44,18 @@ class EntityType extends AbstractType
public function getDefaultOptions(array $options)
{
$defaultOptions = array(
'multiple' => false,
'expanded' => false,
'em' => $this->em,
'em' => null,
'class' => null,
'property' => null,
'query_builder' => null,
'choices' => array(),
'preferred_choices' => array(),
'multiple' => false,
'expanded' => false,
);
$options = array_replace($defaultOptions, $options);
if (!isset($options['choice_list'])) {
$defaultOptions['choice_list'] = new EntityChoiceList(
$options['em'],
$this->registry->getEntityManager($options['em']),
$options['class'],
$options['property'],
$options['query_builder'],
@ -80,4 +75,4 @@ class EntityType extends AbstractType
{
return 'entity';
}
}
}

View File

@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\DoctrineBundle\Logger;
namespace Symfony\Bridge\Doctrine\Logger;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Doctrine\DBAL\Logging\DebugStack;
@ -52,6 +52,6 @@ class DbalLogger extends DebugStack
*/
public function log($message)
{
$this->logger->info($message);
$this->logger->debug($message);
}
}

View File

@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\DoctrineBundle\Mapping\Driver;
namespace Symfony\Bridge\Doctrine\Mapping\Driver;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Mapping\Driver\XmlDriver as BaseXmlDriver;
@ -21,13 +21,49 @@ use Doctrine\ORM\Mapping\Driver\XmlDriver as BaseXmlDriver;
*/
class XmlDriver extends BaseXmlDriver
{
protected $_globalFile = 'mapping';
protected $_prefixes = array();
protected $_globalBasename;
protected $_classCache;
protected $_fileExtension = '.orm.xml';
public function setGlobalBasename($file)
{
$this->_globalBasename = $file;
}
public function getGlobalBasename()
{
return $this->_globalBasename;
}
public function setNamespacePrefixes($prefixes)
{
$this->_prefixes = $prefixes;
}
public function getNamespacePrefixes()
{
return $this->_prefixes;
}
public function isTransient($className)
{
return !in_array($className, $this->getAllClassNames());
if (null === $this->_classCache) {
$this->initialize();
}
// The mapping is defined in the global mapping file
if (isset($this->_classCache[$className])) {
return false;
}
try {
$this->_findMappingFile($className);
return false;
} catch (MappingException $e) {
return true;
}
}
public function getAllClassNames()
@ -39,7 +75,7 @@ class XmlDriver extends BaseXmlDriver
$classes = array();
if ($this->_paths) {
foreach ((array) $this->_paths as $prefix => $path) {
foreach ((array) $this->_paths as $path) {
if (!is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
@ -52,12 +88,16 @@ class XmlDriver extends BaseXmlDriver
foreach ($iterator as $file) {
$fileName = $file->getBasename($this->_fileExtension);
if ($fileName == $file->getBasename() || $fileName == $this->_globalFile) {
if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) {
continue;
}
// NOTE: All files found here means classes are not transient!
$classes[] = $prefix.'\\'.str_replace('.', '\\', $fileName);
if (isset($this->_prefixes[$path])) {
$classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName);
} else {
$classes[] = str_replace('.', '\\', $fileName);
}
}
}
}
@ -81,22 +121,35 @@ class XmlDriver extends BaseXmlDriver
protected function initialize()
{
$this->_classCache = array();
foreach ($this->_paths as $path) {
if (file_exists($file = $path.'/'.$this->_globalFile.$this->_fileExtension)) {
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
if (null !== $this->_globalBasename) {
foreach ($this->_paths as $path) {
if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) {
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
}
}
}
}
protected function _findMappingFile($className)
{
foreach ($this->_paths as $prefix => $path) {
$defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension;
foreach ($this->_paths as $path) {
if (!isset($this->_prefixes[$path])) {
if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) {
return $path.DIRECTORY_SEPARATOR.$defaultFileName;
}
continue;
}
$prefix = $this->_prefixes[$path];
if (0 !== strpos($className, $prefix.'\\')) {
continue;
}
$filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension;
if (file_exists($filename)) {
if (is_file($filename)) {
return $filename;
}

View File

@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\DoctrineBundle\Mapping\Driver;
namespace Symfony\Bridge\Doctrine\Mapping\Driver;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Mapping\Driver\YamlDriver as BaseYamlDriver;
@ -21,13 +21,49 @@ use Doctrine\ORM\Mapping\Driver\YamlDriver as BaseYamlDriver;
*/
class YamlDriver extends BaseYamlDriver
{
protected $_globalFile = 'mapping';
protected $_prefixes = array();
protected $_globalBasename;
protected $_classCache;
protected $_fileExtension = '.orm.yml';
public function setGlobalBasename($file)
{
$this->_globalBasename = $file;
}
public function getGlobalBasename()
{
return $this->_globalBasename;
}
public function setNamespacePrefixes($prefixes)
{
$this->_prefixes = $prefixes;
}
public function getNamespacePrefixes()
{
return $this->_prefixes;
}
public function isTransient($className)
{
return !in_array($className, $this->getAllClassNames());
if (null === $this->_classCache) {
$this->initialize();
}
// The mapping is defined in the global mapping file
if (isset($this->_classCache[$className])) {
return false;
}
try {
$this->_findMappingFile($className);
return false;
} catch (MappingException $e) {
return true;
}
}
public function getAllClassNames()
@ -39,7 +75,7 @@ class YamlDriver extends BaseYamlDriver
$classes = array();
if ($this->_paths) {
foreach ((array) $this->_paths as $prefix => $path) {
foreach ((array) $this->_paths as $path) {
if (!is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
@ -52,12 +88,16 @@ class YamlDriver extends BaseYamlDriver
foreach ($iterator as $file) {
$fileName = $file->getBasename($this->_fileExtension);
if ($fileName == $file->getBasename() || $fileName == $this->_globalFile) {
if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) {
continue;
}
// NOTE: All files found here means classes are not transient!
$classes[] = $prefix.'\\'.str_replace('.', '\\', $fileName);
if (isset($this->_prefixes[$path])) {
$classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName);
} else {
$classes[] = str_replace('.', '\\', $fileName);
}
}
}
}
@ -81,22 +121,35 @@ class YamlDriver extends BaseYamlDriver
protected function initialize()
{
$this->_classCache = array();
foreach ($this->_paths as $path) {
if (file_exists($file = $path.'/'.$this->_globalFile.$this->_fileExtension)) {
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
if (null !== $this->_globalBasename) {
foreach ($this->_paths as $path) {
if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) {
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
}
}
}
}
protected function _findMappingFile($className)
{
foreach ($this->_paths as $prefix => $path) {
$defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension;
foreach ($this->_paths as $path) {
if (!isset($this->_prefixes[$path])) {
if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) {
return $path.DIRECTORY_SEPARATOR.$defaultFileName;
}
continue;
}
$prefix = $this->_prefixes[$path];
if (0 !== strpos($className, $prefix.'\\')) {
continue;
}
$filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension;
if (file_exists($filename)) {
if (is_file($filename)) {
return $filename;
}

View File

@ -0,0 +1,135 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Doctrine;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\ORMException;
/**
* References Doctrine connections and entity managers.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface RegistryInterface
{
/**
* Gets the default connection name.
*
* @return string The default connection name
*/
function getDefaultConnectionName();
/**
* Gets the named connection.
*
* @param string $name The connection name (null for the default one)
*
* @return Connection
*/
function getConnection($name = null);
/**
* Gets an array of all registered connections
*
* @return array An array of Connection instances
*/
function getConnections();
/**
* Gets all connection names.
*
* @return array An array of connection names
*/
function getConnectionNames();
/**
* Gets the default entity manager name.
*
* @return string The default entity manager name
*/
function getDefaultEntityManagerName();
/**
* Gets a named entity manager.
*
* @param string $name The entity manager name (null for the default one)
*
* @return EntityManager
*/
function getEntityManager($name = null);
/**
* Gets an array of all registered entity managers
*
* @return array An array of EntityManager instances
*/
function getEntityManagers();
/**
* Resets a named entity manager.
*
* This method is useful when an entity manager has been closed
* because of a rollbacked transaction AND when you think that
* it makes sense to get a new one to replace the closed one.
*
* Be warned that you will get a brand new entity manager as
* the existing one is not useable anymore. This means that any
* other object with a dependency on this entity manager will
* hold an obsolete reference. You can inject the registry instead
* to avoid this problem.
*
* @param string $name The entity manager name (null for the default one)
*
* @return EntityManager
*/
function resetEntityManager($name = null);
/**
* Resolves a registered namespace alias to the full namespace.
*
* This method looks for the alias in all registered entity managers.
*
* @param string $alias The alias
*
* @return string The full namespace
*
* @see Configuration::getEntityNamespace
*/
function getEntityNamespace($alias);
/**
* Gets all connection names.
*
* @return array An array of connection names
*/
function getEntityManagerNames();
/**
* Gets the EntityRepository for an entity.
*
* @param string $entityName The name of the entity.
* @param string $entityManagerNAme The entity manager name (null for the default one)
*
* @return Doctrine\ORM\EntityRepository
*/
function getRepository($entityName, $entityManagerName = null);
/**
* Gets the entity manager associated with a given class.
*
* @param string $class A Doctrine Entity class name
*
* @return EntityManager|null
*/
function getEntityManagerForClass($class);
}

View File

@ -9,11 +9,13 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\User;
namespace Symfony\Bridge\Doctrine\Security\User;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Wrapper around a Doctrine EntityManager.
@ -66,7 +68,7 @@ class EntityUserProvider implements UserProviderInterface
/**
* {@inheritDoc}
*/
public function loadUser(UserInterface $user)
public function refreshUser(UserInterface $user)
{
if (!$user instanceof $this->class) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));

View File

@ -17,30 +17,31 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Constraint for the Unique Entity validator
*
*
* @Annotation
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class UniqueEntity extends Constraint
{
public $message = 'This value is already used.';
public $message = 'This value is already used';
public $em = null;
public $fields = array();
public function getRequiredOptions()
{
return array('fields');
}
/**
* The validator must be defined as a service with this name.
*
*
* @return string
*/
public function validatedBy()
{
return 'doctrine.orm.validator.unique';
}
/**
* {@inheritDoc}
*/
@ -48,9 +49,9 @@ class UniqueEntity extends Constraint
{
return self::CLASS_CONSTRAINT;
}
public function getDefaultOption()
{
return 'fields';
}
}
}

View File

@ -11,7 +11,7 @@
namespace Symfony\Bridge\Doctrine\Validator\Constraints;
use Symfony\Bundle\DoctrineBundle\Registry;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
@ -19,24 +19,24 @@ use Symfony\Component\Validator\ConstraintValidator;
/**
* Unique Entity Validator checks if one or a set of fields contain unique values.
*
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class UniqueEntityValidator extends ConstraintValidator
{
/**
* @var Registry
* @var RegistryInterface
*/
private $registry;
/**
* @param Registry $registry
* @param RegistryInterface $registry
*/
public function __construct(Registry $registry)
public function __construct(RegistryInterface $registry)
{
$this->registry = $registry;
}
/**
* @param object $entity
* @param Constraint $constraint
@ -47,35 +47,47 @@ class UniqueEntityValidator extends ConstraintValidator
if (!is_array($constraint->fields) && !is_string($constraint->fields)) {
throw new UnexpectedTypeException($constraint->fields, 'array');
}
$fields = (array)$constraint->fields;
if (count($constraint->fields) == 0) {
throw new ConstraintDefinitionException("At least one field has to specified.");
if (count($fields) == 0) {
throw new ConstraintDefinitionException("At least one field has to be specified.");
}
$em = $this->registry->getEntityManager($constraint->em);
$className = $this->context->getCurrentClass();
$class = $em->getClassMetadata($className);
$criteria = array();
foreach ($fields as $fieldName) {
if (!isset($class->reflFields[$fieldName])) {
throw new ConstraintDefinitionException("Only field names mapped by Doctrine can be validated for uniqueness.");
}
$criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity);
if ($criteria[$fieldName] === null) {
return true;
}
}
$repository = $em->getRepository($className);
$result = $repository->findBy($criteria);
if (count($result) > 0 && $result[0] !== $entity) {
$oldPath = $this->context->getPropertyPath();
$this->context->setPropertyPath( empty($oldPath) ? $fields[0] : $oldPath . "." . $fields[0]);
$this->context->addViolation($constraint->message, array(), $criteria[$constraint->fields[0]]);
$this->context->setPropertyPath($oldPath);
/* If no entity matched the query criteria or a single entity matched,
* which is the same as the entity being validated, the criteria is
* unique.
*/
if (0 == count($result) || (1 == count($result) && $entity === $result[0])) {
return true;
}
$oldPath = $this->context->getPropertyPath();
$this->context->setPropertyPath( empty($oldPath) ? $fields[0] : $oldPath.".".$fields[0]);
$this->context->addViolation($constraint->message, array(), $criteria[$fields[0]]);
$this->context->setPropertyPath($oldPath);
return true; // all true, we added the violation already!
}
}
}

View File

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Doctrine\Validator;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Validator\ObjectInitializerInterface;
use Doctrine\ORM\Proxy\Proxy;
/**
* Automatically loads proxy object before validation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class EntityInitializer implements ObjectInitializerInterface
{
protected $registry;
public function __construct(RegistryInterface $registry)
{
$this->registry = $registry;
}
public function initialize($object)
{
if ($object instanceof Proxy) {
$this->registry->getEntityManagerForClass(get_class($object))->getUnitOfWork()->initializeObject($object);
}
}
}

View File

@ -30,10 +30,11 @@ class DebugHandler extends TestHandler implements DebugLoggerInterface
$records = array();
foreach ($this->records as $record) {
$records[] = array(
'timestamp' => $record['datetime']->getTimestamp(),
'message' => $record['message'],
'priority' => $record['level'],
'timestamp' => $record['datetime']->getTimestamp(),
'message' => $record['message'],
'priority' => $record['level'],
'priorityName' => $record['level_name'],
'context' => $record['context'],
);
}

View File

@ -36,7 +36,7 @@ class FirePHPHandler extends BaseFirePHPHandler
/**
* Adds the headers to the response once it's created
*/
public function onCoreResponse(FilterResponseEvent $event)
public function onKernelResponse(FilterResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;

View File

@ -20,14 +20,34 @@ use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Logger extends BaseLogger implements LoggerInterface
class Logger extends BaseLogger implements LoggerInterface, DebugLoggerInterface
{
/**
* @see Symfony\Component\HttpKernel\Log\DebugLoggerInterface
*/
public function getLogs()
{
if ($logger = $this->getDebugLogger()) {
return $logger->getLogs();
}
}
/**
* @see Symfony\Component\HttpKernel\Log\DebugLoggerInterface
*/
public function countErrors()
{
if ($logger = $this->getDebugLogger()) {
return $logger->countErrors();
}
}
/**
* Returns a DebugLoggerInterface instance if one is registered with this logger.
*
* @return DebugLoggerInterface A DebugLoggerInterface instance or null if none is registered
*/
public function getDebugLogger()
private function getDebugLogger()
{
foreach ($this->handlers as $handler) {
if ($handler instanceof DebugLoggerInterface) {

View File

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Monolog\Processor;
use Monolog\Processor\WebProcessor as BaseWebProcessor;
use Symfony\Component\HttpFoundation\Request;
/**
* WebProcessor override to read from the HttpFoundation's Request
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class WebProcessor extends BaseWebProcessor
{
public function __construct(RequestInterface $request)
{
parent::__construct($request->server->all());
}
}

View File

@ -14,6 +14,7 @@ namespace Symfony\Bridge\Twig\Extension;
use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Util\FormUtil;
/**
* FormExtension extends Twig with form capabilities.
@ -24,17 +25,17 @@ use Symfony\Component\Form\Exception\FormException;
class FormExtension extends \Twig_Extension
{
protected $resources;
protected $templates;
protected $blocks;
protected $environment;
protected $themes;
protected $varStack;
protected $template;
public function __construct(array $resources = array())
{
$this->themes = new \SplObjectStorage();
$this->varStack = new \SplObjectStorage();
$this->templates = new \SplObjectStorage();
$this->varStack = array();
$this->blocks = new \SplObjectStorage();
$this->resources = $resources;
}
@ -55,7 +56,7 @@ class FormExtension extends \Twig_Extension
public function setTheme(FormView $view, array $resources)
{
$this->themes->attach($view, $resources);
$this->templates->detach($view);
$this->blocks = new \SplObjectStorage();
}
/**
@ -74,15 +75,27 @@ class FormExtension extends \Twig_Extension
public function getFunctions()
{
return array(
'form_enctype' => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))),
'form_widget' => new \Twig_Function_Method($this, 'renderWidget', array('is_safe' => array('html'))),
'form_errors' => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
'form_label' => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))),
'form_row' => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))),
'form_rest' => new \Twig_Function_Method($this, 'renderRest', array('is_safe' => array('html'))),
'form_enctype' => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))),
'form_widget' => new \Twig_Function_Method($this, 'renderWidget', array('is_safe' => array('html'))),
'form_errors' => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
'form_label' => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))),
'form_row' => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))),
'form_rest' => new \Twig_Function_Method($this, 'renderRest', array('is_safe' => array('html'))),
'_form_is_choice_group' => new \Twig_Function_Method($this, 'isChoiceGroup', array('is_safe' => array('html'))),
'_form_is_choice_selected' => new \Twig_Function_Method($this, 'isChoiceSelected', array('is_safe' => array('html'))),
);
}
public function isChoiceGroup($label)
{
return FormUtil::isChoiceGroup($label);
}
public function isChoiceSelected(FormView $view, $choice)
{
return FormUtil::isChoiceSelected($choice, $view->get('value'));
}
/**
* Renders the HTML enctype in the form tag, if necessary
*
@ -91,6 +104,8 @@ class FormExtension extends \Twig_Extension
* <form action="..." method="post" {{ form_enctype(form) }}>
*
* @param FormView $view The view for which to render the encoding type
*
* @return string The html markup
*/
public function renderEnctype(FormView $view)
{
@ -102,12 +117,22 @@ class FormExtension extends \Twig_Extension
*
* @param FormView $view The view to render as a row
* @param array $variables An array of variables
*
* @return string The html markup
*/
public function renderRow(FormView $view, array $variables = array())
{
return $this->render($view, 'row', $variables);
}
/**
* Renders views which have not already been rendered.
*
* @param FormView $view The parent view
* @param array $variables An array of variables
*
* @return string The html markup
*/
public function renderRest(FormView $view, array $variables = array())
{
return $this->render($view, 'rest', $variables);
@ -120,16 +145,16 @@ class FormExtension extends \Twig_Extension
*
* {{ form_widget(view) }}
*
* You can pass attributes element during the call:
* You can pass options during the call:
*
* {{ form_widget(view, {'class': 'foo'}) }}
* {{ form_widget(view, {'attr': {'class': 'foo'}}) }}
*
* Some fields also accept additional variables as parameters:
* {{ form_widget(view, {'separator': '+++++'}) }}
*
* {{ form_widget(view, {}, {'separator': '+++++'}) }}
*
* @param FormView $view The view to render
* @param FormView $view The view to render
* @param array $variables Additional variables passed to the template
*
* @return string The html markup
*/
public function renderWidget(FormView $view, array $variables = array())
{
@ -140,6 +165,8 @@ class FormExtension extends \Twig_Extension
* Renders the errors of the given view
*
* @param FormView $view The view to render the errors for
*
* @return string The html markup
*/
public function renderErrors(FormView $view)
{
@ -151,97 +178,95 @@ class FormExtension extends \Twig_Extension
*
* @param FormView $view The view to render the label for
* @param string $label Label name
* @param array $variables Additional variables passed to the template
*
* @return string The html markup
*/
public function renderLabel(FormView $view, $label = null)
public function renderLabel(FormView $view, $label = null, array $variables = array())
{
return $this->render($view, 'label', null === $label ? array() : array('label' => $label));
}
protected function render(FormView $view, $section, array $variables = array())
{
$templates = $this->getTemplates($view);
$blocks = $view->get('types');
array_unshift($blocks, '_'.$view->get('id'));
foreach ($blocks as &$block) {
$block = $block.'_'.$section;
if (isset($templates[$block])) {
if ('widget' === $section || 'row' === $section) {
$view->setRendered();
}
$this->varStack[$view] = array_replace(
$view->all(),
isset($this->varStack[$view]) ? $this->varStack[$view] : array(),
$variables
);
$html = $templates[$block]->renderBlock($block, $this->varStack[$view]);
return $html;
}
if ($label !== null) {
$variables += array('label' => $label);
}
throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks)));
return $this->render($view, 'label', $variables);
}
/**
* Returns the templates used by the view.
* Renders a template.
*
* templates are looked for in the following resources:
* * resources from the themes (and its parents)
* * default resources
* 1. This function first looks for a block named "_<view id>_<section>",
* 2. if such a block is not found the function will look for a block named
* "<type name>_<section>",
* 3. the type name is recursively replaced by the parent type name until a
* corresponding block is found
*
* @param FormView $view The view
* @param FormView $view The form view
* @param string $section The section to render (i.e. 'row', 'widget', 'label', ...)
* @param array $variables Additional variables
*
* @return array An array of Twig_TemplateInterface instances
* @return string The html markup
*
* @throws FormException if no template block exists to render the given section of the view
*/
protected function getTemplates(FormView $view)
protected function render(FormView $view, $section, array $variables = array())
{
if (!$this->templates->contains($view)) {
// defaults
$all = $this->resources;
$mainTemplate = in_array($section, array('widget', 'row'));
if ($mainTemplate && $view->isRendered()) {
// themes
$parent = $view;
do {
if (isset($this->themes[$parent])) {
$all = array_merge($all, $this->themes[$parent]);
}
} while ($parent = $parent->getParent());
return '';
}
$templates = array();
foreach ($all as $resource) {
if (!$resource instanceof \Twig_Template) {
$resource = $this->environment->loadTemplate($resource);
}
$blocks = array();
foreach ($this->getBlockNames($resource) as $name) {
$blocks[$name] = $resource;
}
$templates = array_replace($templates, $blocks);
if (null === $this->template) {
$this->template = reset($this->resources);
if (!$this->template instanceof \Twig_Template) {
$this->template = $this->environment->loadTemplate($this->template);
}
}
$this->templates->attach($view, $templates);
$custom = '_'.$view->get('id');
$rendering = $custom.$section;
$blocks = $this->getBlocks($view);
if (isset($this->varStack[$rendering])) {
$typeIndex = $this->varStack[$rendering]['typeIndex'] - 1;
$types = $this->varStack[$rendering]['types'];
$this->varStack[$rendering]['variables'] = array_replace_recursive($this->varStack[$rendering]['variables'], $variables);
} else {
$templates = $this->templates[$view];
$types = $view->get('types');
$types[] = $custom;
$typeIndex = count($types) - 1;
$this->varStack[$rendering] = array (
'variables' => array_replace_recursive($view->all(), $variables),
'types' => $types,
);
}
return $templates;
}
do {
$types[$typeIndex] .= '_'.$section;
protected function getBlockNames($resource)
{
$names = $resource->getBlockNames();
$parent = $resource;
while (false !== $parent = $parent->getParent(array())) {
$names = array_merge($names, $parent->getBlockNames());
}
if (isset($blocks[$types[$typeIndex]])) {
return array_unique($names);
$this->varStack[$rendering]['typeIndex'] = $typeIndex;
// we do not call renderBlock here to avoid too many nested level calls (XDebug limits the level to 100 by default)
ob_start();
$this->template->displayBlock($types[$typeIndex], $this->varStack[$rendering]['variables'], $blocks);
$html = ob_get_clean();
if ($mainTemplate) {
$view->setRendered();
}
unset($this->varStack[$rendering]);
return $html;
}
} while (--$typeIndex >= 0);
throw new FormException(sprintf(
'Unable to render the form as none of the following blocks exist: "%s".',
implode('", "', array_reverse($types))
));
}
/**
@ -253,4 +278,53 @@ class FormExtension extends \Twig_Extension
{
return 'form';
}
/**
* Returns the blocks used to render the view.
*
* Templates are looked for in the resources in the following order:
* * resources from the themes (and its parents)
* * resources from the themes of parent views (up to the root view)
* * default resources
*
* @param FormView $view The view
*
* @return array An array of Twig_TemplateInterface instances
*/
protected function getBlocks(FormView $view)
{
if (!$this->blocks->contains($view)) {
$rootView = !$view->hasParent();
$templates = $rootView ? $this->resources : array();
if (isset($this->themes[$view])) {
$templates = array_merge($templates, $this->themes[$view]);
}
$blocks = array();
foreach ($templates as $template) {
if (!$template instanceof \Twig_Template) {
$template = $this->environment->loadTemplate($template);
}
$templateBlocks = array();
do {
$templateBlocks = array_merge($template->getBlocks(), $templateBlocks);
} while (false !== $template = $template->getParent(array()));
$blocks = array_merge($blocks, $templateBlocks);
}
if (!$rootView) {
$blocks = array_merge($this->getBlocks($view->getParent()), $blocks);
}
$this->blocks->attach($view, $blocks);
} else {
$blocks = $this->blocks[$view];
}
return $blocks;
}
}

View File

@ -40,12 +40,12 @@ class RoutingExtension extends \Twig_Extension
);
}
public function getPath($name, array $parameters = array())
public function getPath($name, $parameters = array())
{
return $this->generator->generate($name, $parameters, false);
}
public function getUrl($name, array $parameters = array())
public function getUrl($name, $parameters = array())
{
return $this->generator->generate($name, $parameters, true);
}

View File

@ -63,14 +63,14 @@ class TranslationExtension extends \Twig_Extension
);
}
public function trans($message, array $arguments = array(), $domain = "messages")
public function trans($message, array $arguments = array(), $domain = "messages", $locale = null)
{
return $this->translator->trans($message, $arguments, $domain);
return $this->translator->trans($message, $arguments, $domain, $locale);
}
public function transchoice($message, $count, array $arguments = array(), $domain = "messages")
public function transchoice($message, $count, array $arguments = array(), $domain = "messages", $locale = null)
{
return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain);
return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale);
}
/**

View File

@ -18,9 +18,9 @@ namespace Symfony\Bridge\Twig\Node;
*/
class TransNode extends \Twig_Node
{
public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $domain, \Twig_Node_Expression $count = null, \Twig_Node_Expression $vars = null, $lineno = 0, $tag = null)
public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $domain, \Twig_Node_Expression $count = null, \Twig_Node_Expression $vars = null, \Twig_Node_Expression $locale = null, $lineno = 0, $tag = null)
{
parent::__construct(array('count' => $count, 'body' => $body, 'domain' => $domain, 'vars' => $vars), array(), $lineno, $tag);
parent::__construct(array('count' => $count, 'body' => $body, 'domain' => $domain, 'vars' => $vars, 'locale' => $locale), array(), $lineno, $tag);
}
/**
@ -71,8 +71,14 @@ class TransNode extends \Twig_Node
$compiler
->raw(', ')
->subcompile($this->getNode('domain'))
->raw(");\n")
;
if (null !== $this->getNode('locale')) {
$compiler
->raw(', ')
->subcompile($this->getNode('locale'))
;
}
$compiler->raw(");\n");
}
protected function compileDefaults(\Twig_Compiler $compiler, \Twig_Node_Expression_Array $defaults)

View File

@ -1,133 +1,68 @@
{% block field_rows %}
{% spaceless %}
{{ form_errors(form) }}
{% for child in form %}
{{ form_row(child) }}
{% endfor %}
{% endspaceless %}
{% endblock field_rows %}
{# Widgets #}
{% block field_enctype %}
{% block form_widget %}
{% spaceless %}
{% if multipart %}enctype="multipart/form-data"{% endif %}
<div {{ block('widget_container_attributes') }}>
{{ block('field_rows') }}
{{ form_rest(form) }}
</div>
{% endspaceless %}
{% endblock field_enctype %}
{% endblock form_widget %}
{% block field_errors %}
{% block collection_widget %}
{% spaceless %}
{% if errors|length > 0 %}
<ul>
{% for error in errors %}
<li>{{ error.messageTemplate|trans(error.messageParameters, 'validators') }}</li>
{% endfor %}
</ul>
{% if prototype is defined %}
{% set attr = attr|merge({'data-prototype': form_row(prototype) }) %}
{% endif %}
{{ block('form_widget') }}
{% endspaceless %}
{% endblock field_errors %}
{% block field_rest %}
{% spaceless %}
{% for child in form %}
{% if not child.rendered %}
{{ form_row(child) }}
{% endif %}
{% endfor %}
{% endspaceless %}
{% endblock field_rest %}
{% block field_label %}
{% spaceless %}
<label for="{{ id }}">{{ label|trans }}</label>
{% endspaceless %}
{% endblock field_label %}
{% block attributes %}
{% spaceless %}
id="{{ id }}" name="{{ full_name }}"{% if read_only %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
{% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %}
{% endspaceless %}
{% endblock attributes %}
{% block container_attributes %}
{% spaceless %}
id="{{ id }}"
{% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %}
{% endspaceless %}
{% endblock container_attributes %}
{% block field_widget %}
{% spaceless %}
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('attributes') }} value="{{ value }}" />
{% endspaceless %}
{% endblock field_widget %}
{% block text_widget %}
{% spaceless %}
{% set type = type|default('text') %}
{{ block('field_widget') }}
{% endspaceless %}
{% endblock text_widget %}
{% block password_widget %}
{% spaceless %}
{% set type = type|default('password') %}
{{ block('field_widget') }}
{% endspaceless %}
{% endblock password_widget %}
{% block hidden_widget %}
{% set type = type|default('hidden') %}
{{ block('field_widget') }}
{% endblock hidden_widget %}
{% block hidden_row %}
{{ form_widget(form) }}
{% endblock hidden_row %}
{% endblock collection_widget %}
{% block textarea_widget %}
{% spaceless %}
<textarea {{ block('attributes') }}>{{ value }}</textarea>
<textarea {{ block('widget_attributes') }}>{{ value }}</textarea>
{% endspaceless %}
{% endblock textarea_widget %}
{% block options %}
{% block widget_choice_options %}
{% spaceless %}
{% for choice, label in options %}
{% if form.choiceGroup(label) %}
<optgroup label="{{ choice }}">
{% if _form_is_choice_group(label) %}
<optgroup label="{{ choice|trans }}">
{% for nestedChoice, nestedLabel in label %}
<option value="{{ nestedChoice }}"{% if form.choiceSelected(nestedChoice) %} selected="selected"{% endif %}>{{ nestedLabel }}</option>
<option value="{{ nestedChoice }}"{% if _form_is_choice_selected(form, nestedChoice) %} selected="selected"{% endif %}>{{ nestedLabel|trans }}</option>
{% endfor %}
</optgroup>
{% else %}
<option value="{{ choice }}"{% if form.choiceSelected(choice) %} selected="selected"{% endif %}>{{ label }}</option>
<option value="{{ choice }}"{% if _form_is_choice_selected(form, choice) %} selected="selected"{% endif %}>{{ label|trans }}</option>
{% endif %}
{% endfor %}
{% endspaceless %}
{% endblock options %}
{% endblock widget_choice_options %}
{% block choice_widget %}
{% spaceless %}
{% if expanded %}
<div {{ block('container_attributes') }}>
{% for choice, child in form %}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
{{ form_widget(child) }}
{{ form_label(child) }}
{% endfor %}
</div>
{% else %}
<select {{ block('attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if not multiple and not required %}
<option value="">{{ empty_value }}</option>
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if empty_value is not none %}
<option value="">{{ empty_value|trans }}</option>
{% endif %}
{% if preferred_choices|length > 0 %}
{% set options = preferred_choices %}
{{ block('options') }}
<option disabled="disabled">{{ separator }}</option>
{{ block('widget_choice_options') }}
{% if choices|length > 0 %}
<option disabled="disabled">{{ separator }}</option>
{% endif %}
{% endif %}
{% set options = choices %}
{{ block('options') }}
{{ block('widget_choice_options') }}
</select>
{% endif %}
{% endspaceless %}
@ -135,37 +70,41 @@
{% block checkbox_widget %}
{% spaceless %}
<input type="checkbox" {{ block('attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
<input type="checkbox" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
{% endspaceless %}
{% endblock checkbox_widget %}
{% block radio_widget %}
{% spaceless %}
<input type="radio" {{ block('attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
<input type="radio" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
{% endspaceless %}
{% endblock radio_widget %}
{% block datetime_widget %}
{% spaceless %}
<div {{ block('container_attributes') }}>
{{ form_errors(form.date) }}
{{ form_errors(form.time) }}
{{ form_widget(form.date) }}
{{ form_widget(form.time) }}
</div>
{% if widget == 'single_text' %}
{{ block('field_widget') }}
{% else %}
<div {{ block('widget_container_attributes') }}>
{{ form_errors(form.date) }}
{{ form_errors(form.time) }}
{{ form_widget(form.date) }}
{{ form_widget(form.time) }}
</div>
{% endif %}
{% endspaceless %}
{% endblock datetime_widget %}
{% block date_widget %}
{% spaceless %}
{% if widget == 'single_text' %}
{{ block('text_widget') }}
{{ block('field_widget') }}
{% else %}
<div {{ block('container_attributes') }}>
<div {{ block('widget_container_attributes') }}>
{{ date_pattern|replace({
'{{ year }}': form_widget(form.year),
'{{ year }}': form_widget(form.year),
'{{ month }}': form_widget(form.month),
'{{ day }}': form_widget(form.day),
'{{ day }}': form_widget(form.day),
})|raw }}
</div>
{% endif %}
@ -174,9 +113,13 @@
{% block time_widget %}
{% spaceless %}
<div {{ block('container_attributes') }}>
{{ form_widget(form.hour, { 'attr': { 'size': '1' } }) }}:{{ form_widget(form.minute, { 'attr': { 'size': '1' } }) }}{% if with_seconds %}:{{ form_widget(form.second, { 'attr': { 'size': '1' } }) }}{% endif %}
</div>
{% if widget == 'single_text' %}
{{ block('field_widget') }}
{% else %}
<div {{ block('widget_container_attributes') }}>
{{ form_widget(form.hour, { 'attr': { 'size': '1' } }) }}:{{ form_widget(form.minute, { 'attr': { 'size': '1' } }) }}{% if with_seconds %}:{{ form_widget(form.second, { 'attr': { 'size': '1' } }) }}{% endif %}
</div>
{% endif %}
{% endspaceless %}
{% endblock time_widget %}
@ -222,22 +165,57 @@
{% endspaceless %}
{% endblock percent_widget %}
{% block file_widget %}
{% block field_widget %}
{% spaceless %}
<div {{ block('container_attributes') }}>
{{ form_widget(form.file) }}
{{ form_widget(form.token) }}
{{ form_widget(form.name) }}
{{ form_widget(form.originalName) }}
</div>
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('widget_attributes') }} value="{{ value }}" />
{% endspaceless %}
{% endblock file_widget %}
{% endblock field_widget %}
{% block collection_widget %}
{% block password_widget %}
{% spaceless %}
{{ block('form_widget') }}
{% set type = type|default('password') %}
{{ block('field_widget') }}
{% endspaceless %}
{% endblock collection_widget %}
{% endblock password_widget %}
{% block hidden_widget %}
{% set type = type|default('hidden') %}
{{ block('field_widget') }}
{% endblock hidden_widget %}
{% block email_widget %}
{% spaceless %}
{% set type = type|default('email') %}
{{ block('field_widget') }}
{% endspaceless %}
{% endblock email_widget %}
{# Labels #}
{% block generic_label %}
{% spaceless %}
{% if required %}
{% set attr = attr|merge({'class': attr.class|default('') ~ ' required'}) %}
{% endif %}
<label{% for attrname,attrvalue in attr %} {{attrname}}="{{attrvalue}}"{% endfor %}>{{ label|trans }}</label>
{% endspaceless %}
{% endblock %}
{% block field_label %}
{% spaceless %}
{% set attr = attr|merge({'for': id}) %}
{{ block('generic_label') }}
{% endspaceless %}
{% endblock field_label %}
{% block form_label %}
{% spaceless %}
{{ block('generic_label') }}
{% endspaceless %}
{% endblock form_label %}
{# Rows #}
{% block repeated_row %}
{% spaceless %}
@ -245,29 +223,71 @@
{% endspaceless %}
{% endblock repeated_row %}
{% block field_row %}
{% spaceless %}
<div>
{{ form_label(form) }}
{{ form_label(form, label|default(null)) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock field_row %}
{% block form_widget %}
{% spaceless %}
<div {{ block('container_attributes') }}>
{{ block('field_rows') }}
{{ form_rest(form) }}
</div>
{% endspaceless %}
{% endblock form_widget %}
{% block hidden_row %}
{{ form_widget(form) }}
{% endblock hidden_row %}
{% block email_widget %}
{# Misc #}
{% block field_enctype %}
{% spaceless %}
{% set type = type|default('email') %}
{{ block('field_widget') }}
{% if multipart %}enctype="multipart/form-data"{% endif %}
{% endspaceless %}
{% endblock email_widget %}
{% endblock field_enctype %}
{% block field_errors %}
{% spaceless %}
{% if errors|length > 0 %}
<ul>
{% for error in errors %}
<li>{{ error.messageTemplate|trans(error.messageParameters, 'validators') }}</li>
{% endfor %}
</ul>
{% endif %}
{% endspaceless %}
{% endblock field_errors %}
{% block field_rest %}
{% spaceless %}
{% for child in form %}
{% if not child.rendered %}
{{ form_row(child) }}
{% endif %}
{% endfor %}
{% endspaceless %}
{% endblock field_rest %}
{# Support #}
{% block field_rows %}
{% spaceless %}
{{ form_errors(form) }}
{% for child in form %}
{{ form_row(child) }}
{% endfor %}
{% endspaceless %}
{% endblock field_rows %}
{% block widget_attributes %}
{% spaceless %}
id="{{ id }}" name="{{ full_name }}"{% if read_only %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
{% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %}
{% endspaceless %}
{% endblock widget_attributes %}
{% block widget_container_attributes %}
{% spaceless %}
id="{{ id }}"
{% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %}
{% endspaceless %}
{% endblock widget_container_attributes %}

View File

@ -1,10 +1,10 @@
{% extends "TwigBundle:Form:div_layout.html.twig" %}
{% use "form_div_layout.html.twig" %}
{% block field_row %}
{% spaceless %}
<tr>
<td>
{{ form_label(form) }}
{{ form_label(form, label|default(null)) }}
</td>
<td>
{{ form_errors(form) }}
@ -36,15 +36,9 @@
{% endspaceless %}
{% endblock hidden_row %}
{% block repeated_errors %}
{% spaceless %}
{{ block('form_errors') }}
{% endspaceless %}
{% endblock repeated_errors %}
{% block form_widget %}
{% spaceless %}
<table {{ block('container_attributes') }}>
<table {{ block('widget_container_attributes') }}>
{{ block('field_rows') }}
{{ form_rest(form) }}
</table>

View File

@ -37,6 +37,7 @@ class TransChoiceTokenParser extends TransTokenParser
$count = $this->parser->getExpressionParser()->parseExpression();
$domain = new \Twig_Node_Expression_Constant('messages', $lineno);
$locale = null;
if ($stream->test('with')) {
// {% transchoice count with vars %}
@ -50,6 +51,12 @@ class TransChoiceTokenParser extends TransTokenParser
$domain = $this->parser->getExpressionParser()->parseExpression();
}
if ($stream->test('into')) {
// {% transchoice count into "fr" %}
$stream->next();
$locale = $this->parser->getExpressionParser()->parseExpression();
}
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideTransChoiceFork'), true);
@ -60,7 +67,7 @@ class TransChoiceTokenParser extends TransTokenParser
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return new TransNode($body, $domain, $count, $vars, $lineno, $this->getTag());
return new TransNode($body, $domain, $count, $vars, $locale, $lineno, $this->getTag());
}
public function decideTransChoiceFork($token)

View File

@ -34,6 +34,7 @@ class TransTokenParser extends \Twig_TokenParser
$vars = new \Twig_Node_Expression_Array(array(), $lineno);
$domain = new \Twig_Node_Expression_Constant('messages', $lineno);
$locale = null;
if (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) {
if ($stream->test('with')) {
// {% trans with vars %}
@ -45,6 +46,12 @@ class TransTokenParser extends \Twig_TokenParser
// {% trans from "messages" %}
$stream->next();
$domain = $this->parser->getExpressionParser()->parseExpression();
}
if ($stream->test('into')) {
// {% trans into "fr" %}
$stream->next();
$locale = $this->parser->getExpressionParser()->parseExpression();
} elseif (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) {
throw new \Twig_Error_Syntax('Unexpected token. Twig was looking for the "with" or "from" keyword.');
}
@ -60,7 +67,7 @@ class TransTokenParser extends \Twig_TokenParser
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return new TransNode($body, $domain, null, $vars, $lineno, $this->getTag());
return new TransNode($body, $domain, null, $vars, $locale, $lineno, $this->getTag());
}
public function decideTransFork($token)

View File

@ -1,43 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\AssetFactoryPass;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\AssetManagerPass;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\CheckYuiFilterPass;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\FilterManagerPass;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\CheckCssEmbedFilterPass;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\CheckClosureFilterPass;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\TemplatingPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Assetic integration.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AsseticBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new CheckClosureFilterPass());
$container->addCompilerPass(new CheckCssEmbedFilterPass());
$container->addCompilerPass(new CheckYuiFilterPass());
$container->addCompilerPass(new TemplatingPass());
$container->addCompilerPass(new AssetFactoryPass());
$container->addCompilerPass(new AssetManagerPass());
$container->addCompilerPass(new FilterManagerPass());
}
}

View File

@ -1,41 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\CacheWarmer;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* The AssetManagerCacheWarmer warms up the formula loader.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AssetManagerCacheWarmer implements CacheWarmerInterface
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function warmUp($cacheDir)
{
$am = $this->container->get('assetic.asset_manager');
$am->load();
}
public function isOptional()
{
return true;
}
}

View File

@ -1,44 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\CacheWarmer;
use Assetic\AssetWriter;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* The AssetWriterCacheWarmer processes and writes the asset files.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AssetWriterCacheWarmer implements CacheWarmerInterface
{
private $container;
private $writer;
public function __construct(ContainerInterface $container, AssetWriter $writer)
{
$this->container = $container;
$this->writer = $writer;
}
public function warmUp($cacheDir)
{
$am = $this->container->get('assetic.asset_manager');
$this->writer->writeManagerAssets($am);
}
public function isOptional()
{
return true;
}
}

View File

@ -1,194 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Command;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\LazyAssetManager;
use Symfony\Bundle\FrameworkBundle\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Dumps assets to the filesystem.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class DumpCommand extends Command
{
private $basePath;
private $am;
protected function configure()
{
$this
->setName('assetic:dump')
->setDescription('Dumps all assets to the filesystem')
->addArgument('write_to', InputArgument::OPTIONAL, 'Override the configured asset root')
->addOption('watch', null, InputOption::VALUE_NONE, 'Check for changes every second, debug mode only')
->addOption('force', null, InputOption::VALUE_NONE, 'Force an initial generation of all assets (used with --watch)')
->addOption('period', null, InputOption::VALUE_REQUIRED, 'Set the polling period in seconds (used with --watch)', 1)
;
}
protected function initialize(InputInterface $input, OutputInterface $output)
{
parent::initialize($input, $output);
$this->basePath = $input->getArgument('write_to') ?: $this->container->getParameter('assetic.write_to');
$this->am = $this->container->get('assetic.asset_manager');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln(sprintf('Dumping all <comment>%s</comment> assets.', $input->getOption('env')));
$output->writeln(sprintf('Debug mode is <comment>%s</comment>.', $input->getOption('no-debug') ? 'off' : 'on'));
$output->writeln('');
if (!$input->getOption('watch')) {
foreach ($this->am->getNames() as $name) {
$this->dumpAsset($name, $output);
}
return;
}
if (!$this->am->isDebug()) {
throw new \RuntimeException('The --watch option is only available in debug mode.');
}
$this->watch($input, $output);
}
/**
* Watches a asset manager for changes.
*
* This method includes an infinite loop the continuously polls the asset
* manager for changes.
*
* @param InputInterface $input The command input
* @param OutputInterface $output The command output
*/
private function watch(InputInterface $input, OutputInterface $output)
{
$refl = new \ReflectionClass('Assetic\\AssetManager');
$prop = $refl->getProperty('assets');
$prop->setAccessible(true);
$cache = sys_get_temp_dir().'/assetic_watch_'.substr(sha1($this->basePath), 0, 7);
if ($input->getOption('force') || !file_exists($cache)) {
$previously = array();
} else {
$previously = unserialize(file_get_contents($cache));
}
$error = '';
while (true) {
try {
foreach ($this->am->getNames() as $name) {
if ($this->checkAsset($name, $previously)) {
$this->dumpAsset($name, $output);
}
}
// reset the asset manager
$prop->setValue($this->am, array());
$this->am->load();
file_put_contents($cache, serialize($previously));
$error = '';
sleep($input->getOption('period'));
} catch (\Exception $e) {
if ($error != $msg = $e->getMessage()) {
$output->writeln('<error>[error]</error> '.$msg);
$error = $msg;
}
}
}
}
/**
* Checks if an asset should be dumped.
*
* @param string $name The asset name
* @param array &$previously An array of previous visits
*
* @return Boolean Whether the asset should be dumped
*/
private function checkAsset($name, array &$previously)
{
$formula = $this->am->hasFormula($name) ? serialize($this->am->getFormula($name)) : null;
$asset = $this->am->get($name);
$mtime = $asset->getLastModified();
if (isset($previously[$name])) {
$changed = $previously[$name]['mtime'] != $mtime || $previously[$name]['formula'] != $formula;
} else {
$changed = true;
}
$previously[$name] = array('mtime' => $mtime, 'formula' => $formula);
return $changed;
}
/**
* Writes an asset.
*
* If the application or asset is in debug mode, each leaf asset will be
* dumped as well.
*
* @param string $name An asset name
* @param OutputInterface $output The command output
*/
private function dumpAsset($name, OutputInterface $output)
{
$asset = $this->am->get($name);
$formula = $this->am->getFormula($name);
// start by dumping the main asset
$this->doDump($asset, $output);
// dump each leaf if debug
if (isset($formula[2]['debug']) ? $formula[2]['debug'] : $this->am->isDebug()) {
foreach ($asset as $leaf) {
$this->doDump($leaf, $output);
}
}
}
/**
* Performs the asset dump.
*
* @param AssetInterface $asset An asset
* @param OutputInterface $output The command output
*
* @throws RuntimeException If there is a problem writing the asset
*/
private function doDump(AssetInterface $asset, OutputInterface $output)
{
$target = rtrim($this->basePath, '/').'/'.str_replace('_controller/', '', $asset->getTargetPath());
if (!is_dir($dir = dirname($target))) {
$output->writeln('<info>[dir+]</info> '.$dir);
if (false === @mkdir($dir, 0777, true)) {
throw new \RuntimeException('Unable to create directory '.$dir);
}
}
$output->writeln('<info>[file+]</info> '.$target);
if (false === @file_put_contents($target, $asset->dump())) {
throw new \RuntimeException('Unable to write file '.$target);
}
}
}

View File

@ -1,95 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Controller;
use Assetic\Asset\AssetCache;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\LazyAssetManager;
use Assetic\Cache\CacheInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Serves assets.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AsseticController
{
protected $request;
protected $am;
protected $cache;
public function __construct(Request $request, LazyAssetManager $am, CacheInterface $cache)
{
$this->request = $request;
$this->am = $am;
$this->cache = $cache;
}
public function render($name, $pos = null)
{
if (!$this->am->has($name)) {
throw new NotFoundHttpException(sprintf('The "%s" asset could not be found.', $name));
}
$asset = $this->am->get($name);
if (null !== $pos && !$asset = $this->findAssetLeaf($asset, $pos)) {
throw new NotFoundHttpException(sprintf('The "%s" asset does not include a leaf at position %d.', $name, $pos));
}
$response = $this->createResponse();
// last-modified
if (null !== $lastModified = $asset->getLastModified()) {
$date = new \DateTime();
$date->setTimestamp($lastModified);
$response->setLastModified($date);
}
// etag
if ($this->am->hasFormula($name)) {
$formula = $this->am->getFormula($name);
$formula['last_modified'] = $lastModified;
$response->setETag(md5(serialize($formula)));
}
if ($response->isNotModified($this->request)) {
return $response;
}
$response->setContent($this->cachifyAsset($asset)->dump());
return $response;
}
protected function createResponse()
{
return new Response();
}
protected function cachifyAsset(AssetInterface $asset)
{
return new AssetCache($asset, $this->cache);
}
private function findAssetLeaf(\Traversable $asset, $pos)
{
$i = 0;
foreach ($asset as $leaf) {
if ($pos == $i++) {
return $leaf;
}
}
}
}

View File

@ -1,231 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/**
* Semantic asset configuration.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AsseticExtension extends Extension
{
/**
* Loads the configuration.
*
* @param array $configs An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
public function load(array $configs, ContainerBuilder $container)
{
$parameterBag = $container->getParameterBag();
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('assetic.xml');
$loader->load('templating_twig.xml');
$loader->load('templating_php.xml');
$config = self::processConfigs($configs, $container->getParameter('kernel.debug'), array_keys($container->getParameter('kernel.bundles')));
$container->setParameter('assetic.debug', $config['debug']);
$container->setParameter('assetic.use_controller', $config['use_controller']);
$container->setParameter('assetic.read_from', $config['read_from']);
$container->setParameter('assetic.write_to', $config['write_to']);
$container->setParameter('assetic.java.bin', $config['java']);
$container->setParameter('assetic.node.bin', $config['node']);
$container->setParameter('assetic.sass.bin', $config['sass']);
// register formulae
$formulae = array();
foreach ($config['assets'] as $name => $formula) {
$formulae[$name] = array($formula['inputs'], $formula['filters'], $formula['options']);
}
if ($formulae) {
$container->getDefinition('assetic.config_resource')->replaceArgument(0, $formulae);
} else {
$container->removeDefinition('assetic.config_loader');
$container->removeDefinition('assetic.config_resource');
}
// register filters
foreach ($config['filters'] as $name => $filter) {
if (isset($filter['resource'])) {
$loader->load($parameterBag->resolveValue($filter['resource']));
unset($filter['resource']);
} else {
$loader->load('filters/'.$name.'.xml');
}
if (isset($filter['file'])) {
$container->getDefinition('assetic.filter.'.$name)->setFile($filter['file']);
unset($filter['file']);
}
if (isset($filter['apply_to'])) {
if (!is_array($filter['apply_to'])) {
$filter['apply_to'] = array($filter['apply_to']);
}
foreach ($filter['apply_to'] as $i => $pattern) {
$worker = new DefinitionDecorator('assetic.worker.ensure_filter');
$worker->replaceArgument(0, '/'.$pattern.'/');
$worker->replaceArgument(1, new Reference('assetic.filter.'.$name));
$worker->addTag('assetic.factory_worker');
$container->setDefinition('assetic.filter.'.$name.'.worker'.$i, $worker);
}
unset($filter['apply_to']);
}
foreach ($filter as $key => $value) {
$container->setParameter('assetic.filter.'.$name.'.'.$key, $value);
}
}
// twig functions
$container->setParameter('assetic.twig_extension.functions', $config['twig']['functions']);
// choose dynamic or static
if ($parameterBag->resolveValue($parameterBag->get('assetic.use_controller'))) {
$loader->load('controller.xml');
$container->getDefinition('assetic.helper.dynamic')->addTag('templating.helper', array('alias' => 'assetic'));
$container->removeDefinition('assetic.helper.static');
} else {
$loader->load('asset_writer.xml');
$container->getDefinition('assetic.helper.static')->addTag('templating.helper', array('alias' => 'assetic'));
$container->removeDefinition('assetic.helper.dynamic');
}
// register config resources
self::registerFormulaResources($container, $parameterBag->resolveValue($config['bundles']));
}
/**
* Merges the user's config arrays.
*
* @param array $configs An array of config arrays
* @param Boolean $debug The debug mode
* @param array $bundles An array of all bundle names
*
* @return array The merged config
*/
static protected function processConfigs(array $configs, $debug, array $bundles)
{
$processor = new Processor();
$configuration = new Configuration($debug, $bundles);
return $processor->processConfiguration($configuration, $configs);
}
/**
* Registers factory resources for certain bundles.
*
* @param ContainerBuilder $container The container
* @param array $bundles An array of select bundle names
*
* @throws InvalidArgumentException If registering resources from a bundle that doesn't exist
*/
static protected function registerFormulaResources(ContainerBuilder $container, array $bundles)
{
$map = $container->getParameter('kernel.bundles');
$am = $container->getDefinition('assetic.asset_manager');
// bundle views/ directories and kernel overrides
foreach ($bundles as $name) {
$rc = new \ReflectionClass($map[$name]);
foreach (array('twig', 'php') as $engine) {
$container->setDefinition(
'assetic.'.$engine.'_directory_resource.'.$name,
self::createDirectoryResourceDefinition($name, $engine, array(
$container->getParameter('kernel.root_dir').'/Resources/'.$name.'/views',
dirname($rc->getFileName()).'/Resources/views',
))
);
}
}
// kernel views/ directory
foreach (array('twig', 'php') as $engine) {
$container->setDefinition(
'assetic.'.$engine.'_directory_resource.kernel',
self::createDirectoryResourceDefinition('', $engine, array(
$container->getParameter('kernel.root_dir').'/Resources/views',
))
);
}
}
/**
* Creates a directory resource definition.
*
* If more than one directory is provided a coalescing definition will be
* returned.
*
* @param string $bundle A bundle name or empty string
* @param string $engine The templating engine
* @param array $dirs An array of directories to merge
*
* @return Definition A resource definition
*/
static protected function createDirectoryResourceDefinition($bundle, $engine, array $dirs)
{
$dirResources = array();
foreach ($dirs as $dir) {
$dirResources[] = $dirResource = new Definition('%assetic.directory_resource.class%');
$dirResource
->addArgument(new Reference('templating.loader'))
->addArgument($bundle)
->addArgument($dir)
->addArgument('/^[^.]+\.[^.]+\.'.$engine.'$/')
->setPublic(false);
}
if (1 == count($dirResources)) {
// no need to coalesce
$definition = $dirResources[0];
} else {
$definition = new Definition('%assetic.coalescing_directory_resource.class%');
$definition
->addArgument($dirResources)
->setPublic(false);
}
return $definition
->addTag('assetic.templating.'.$engine)
->addTag('assetic.formula_resource', array('loader' => $engine));
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__ . '/../Resources/config/schema';
}
public function getNamespace()
{
return 'http://symfony.com/schema/dic/assetic';
}
}

View File

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds services tagged as workers to the asset factory.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AssetFactoryPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('assetic.asset_factory')) {
return;
}
$factory = $container->getDefinition('assetic.asset_factory');
foreach ($container->findTaggedServiceIds('assetic.factory_worker') as $id => $attr) {
$factory->addMethodCall('addWorker', array(new Reference($id)));
}
}
}

View File

@ -1,62 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds services tagged as assets to the asset manager.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AssetManagerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('assetic.asset_manager')) {
return;
}
$am = $container->getDefinition('assetic.asset_manager');
// add assets
foreach ($container->findTaggedServiceIds('assetic.asset') as $id => $attributes) {
foreach ($attributes as $attr) {
if (isset($attr['alias'])) {
$am->addMethodCall('set', array($attr['alias'], new Reference($id)));
}
}
}
// add loaders
$loaders = array();
foreach ($container->findTaggedServiceIds('assetic.formula_loader') as $id => $attributes) {
foreach ($attributes as $attr) {
if (isset($attr['alias'])) {
$loaders[$attr['alias']] = new Reference($id);
}
}
}
$am->replaceArgument(1, $loaders);
// add resources
foreach ($container->findTaggedServiceIds('assetic.formula_resource') as $id => $attributes) {
foreach ($attributes as $attr) {
if (isset($attr['loader'])) {
$am->addMethodCall('addResource', array(new Reference($id), $attr['loader']));
}
}
}
}
}

View File

@ -1,34 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Tags either the closure JAR or API filter for the filter manager.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class CheckClosureFilterPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if ($container->hasDefinition('assetic.filter.closure.jar')
&& $container->hasParameter('assetic.filter.closure.jar')
&& $container->getParameterBag()->resolveValue($container->getParameter('assetic.filter.closure.jar'))) {
$container->removeDefinition('assetic.filter.closure.api');
} elseif ($container->hasDefinition('assetic.filter.closure.api')) {
$container->removeDefinition('assetic.filter.closure.jar');
}
}
}

View File

@ -1,31 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Checks that the location of the CssEmbed JAR has been configured.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class CheckCssEmbedFilterPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if ($container->hasDefinition('assetic.filter.cssembed') &&
!$container->getParameterBag()->resolveValue($container->getParameter('assetic.filter.cssembed.jar'))) {
throw new \RuntimeException('The "assetic.filters.cssembed" configuration requires a "jar" value.');
}
}
}

View File

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Checks that the location of the YUI JAR has been configured.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class CheckYuiFilterPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if ($container->hasDefinition('assetic.filter.yui_css') &&
!$container->getParameterBag()->resolveValue($container->getParameter('assetic.filter.yui_css.jar'))) {
throw new \RuntimeException('The "assetic.filters.yui_css" configuration requires a "jar" value.');
}
if ($container->hasDefinition('assetic.filter.yui_js') &&
!$container->getParameterBag()->resolveValue($container->getParameter('assetic.filter.yui_js.jar'))) {
throw new \RuntimeException('The "assetic.filters.yui_js" configuration requires a "jar" value.');
}
}
}

View File

@ -1,44 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds services tagged as filters to the filter manager.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class FilterManagerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('assetic.filter_manager')) {
return;
}
$mapping = array();
foreach ($container->findTaggedServiceIds('assetic.filter') as $id => $attributes) {
foreach ($attributes as $attr) {
if (isset($attr['alias'])) {
$mapping[$attr['alias']] = $id;
}
}
}
$container
->getDefinition('assetic.filter_manager')
->replaceArgument(1, $mapping);
}
}

View File

@ -1,44 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* This pass removes services associated with unused templating engines.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class TemplatingPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('assetic.asset_manager')) {
return;
}
$engines = $container->getParameterBag()->resolveValue($container->getParameter('templating.engines'));
if (!in_array('twig', $engines)) {
foreach ($container->findTaggedServiceIds('assetic.templating.twig') as $id => $attr) {
$container->removeDefinition($id);
}
}
if (!in_array('php', $engines)) {
foreach ($container->findTaggedServiceIds('assetic.templating.php') as $id => $attr) {
$container->removeDefinition($id);
}
}
}
}

View File

@ -1,178 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\AsseticBundle\DependencyInjection;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This class contains the configuration information for the bundle
*
* This information is solely responsible for how the different configuration
* sections are normalized, and merged.
*
* @author Christophe Coevoet <stof@notk.org>
* @author Kris Wallsmith <kris@symfony.com>
*/
class Configuration implements ConfigurationInterface
{
private $bundles;
private $debug;
/**
* Constructor
*
* @param Boolean $debug Whether to use the debug mode
* @param array $bundles An array of bundle names
*/
public function __construct($debug, array $bundles)
{
$this->debug = (Boolean) $debug;
$this->bundles = $bundles;
}
/**
* Generates the configuration tree builder.
*
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
*/
public function getConfigTreeBuilder()
{
$builder = new TreeBuilder();
$finder = new ExecutableFinder();
$builder->root('assetic')
->children()
->booleanNode('debug')->defaultValue($this->debug)->end()
->booleanNode('use_controller')->defaultValue($this->debug)->end()
->scalarNode('read_from')->defaultValue('%kernel.root_dir%/../web')->end()
->scalarNode('write_to')->defaultValue('%assetic.read_from%')->end()
->scalarNode('java')->defaultValue($finder->find('java', '/usr/bin/java'))->end()
->scalarNode('node')->defaultValue($finder->find('node', '/usr/bin/node'))->end()
->scalarNode('sass')->defaultValue($finder->find('sass', '/usr/bin/sass'))->end()
->end()
// bundles
->fixXmlConfig('bundle')
->children()
->arrayNode('bundles')
->defaultValue($this->bundles)
->requiresAtLeastOneElement()
->prototype('scalar')
->validate()
->ifNotInArray($this->bundles)
->thenInvalid('%s is not a valid bundle.')
->end()
->end()
->end()
->end()
// assets
->fixXmlConfig('asset')
->children()
->arrayNode('assets')
->addDefaultsIfNotSet()
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('array')
->beforeNormalization()
// a scalar is a simple formula of one input file
->ifTrue(function($v) { return !is_array($v); })
->then(function($v) { return array('inputs' => array($v)); })
->end()
->beforeNormalization()
->always()
->then(function($v)
{
// cast scalars as array
foreach (array('input', 'inputs', 'filter', 'filters') as $key) {
if (isset($v[$key]) && !is_array($v[$key])) {
$v[$key] = array($v[$key]);
}
}
// organize arbitrary options
foreach ($v as $key => $value) {
if (!in_array($key, array('input', 'inputs', 'filter', 'filters', 'option', 'options'))) {
$v['options'][$key] = $value;
unset($v[$key]);
}
}
return $v;
})
->end()
// the formula
->fixXmlConfig('input')
->fixXmlConfig('filter')
->children()
->arrayNode('inputs')
->prototype('scalar')->end()
->end()
->arrayNode('filters')
->prototype('scalar')->end()
->end()
->arrayNode('options')
->useAttributeAsKey('name')
->prototype('variable')->end()
->end()
->end()
->end()
->end()
->end()
// filters
->fixXmlConfig('filter')
->children()
->arrayNode('filters')
->addDefaultsIfNotSet()
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('variable')
->treatNullLike(array())
->validate()
->ifTrue(function($v) { return !is_array($v); })
->thenInvalid('The assetic.filters config %s must be either null or an array.')
->end()
->end()
->end()
->end()
// twig
->children()
->arrayNode('twig')
->addDefaultsIfNotSet()
->defaultValue(array())
->fixXmlConfig('function')
->children()
->arrayNode('functions')
->addDefaultsIfNotSet()
->defaultValue(array())
->useAttributeAsKey('name')
->prototype('variable')
->treatNullLike(array())
->validate()
->ifTrue(function($v) { return !is_array($v); })
->thenInvalid('The assetic.twig.functions config %s must be either null or an array.')
->end()
->end()
->end()
->end()
->end()
->end()
;
return $builder;
}
}

View File

@ -1,91 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory;
use Assetic\Factory\AssetFactory as BaseAssetFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* Loads asset formulae from the filesystem.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AssetFactory extends BaseAssetFactory
{
private $kernel;
private $container;
/**
* Constructor.
*
* @param KernelInterface $kernel The kernel is used to parse bundle notation
* @param ContainerInterface $container The container is used to load the managers lazily, thus avoiding a circular dependency
* @param string $baseDir The base directory for relative inputs
* @param Boolean $debug The current debug mode
*/
public function __construct(KernelInterface $kernel, ContainerInterface $container, $baseDir, $debug = false)
{
$this->kernel = $kernel;
$this->container = $container;
parent::__construct($baseDir, $debug);
}
/**
* Adds support for bundle notation file and glob assets.
*
* FIXME: This is a naive implementation of globs in that it doesn't
* attempt to support bundle inheritance within the glob pattern itself.
*/
protected function parseInput($input, array $options = array())
{
// expand bundle notation
if ('@' == $input[0] && false !== strpos($input, '/')) {
// use the bundle path as this asset's root
$bundle = substr($input, 1);
if (false !== $pos = strpos($bundle, '/')) {
$bundle = substr($bundle, 0, $pos);
}
$options['root'] = array($this->kernel->getBundle($bundle)->getPath());
// canonicalize the input
if (false !== $pos = strpos($input, '*')) {
// locateResource() does not support globs so we provide a naive implementation here
list($before, $after) = explode('*', $input, 2);
$input = $this->kernel->locateResource($before).'*'.$after;
} else {
$input = $this->kernel->locateResource($input);
}
}
return parent::parseInput($input, $options);
}
protected function createAssetReference($name)
{
if (!$this->getAssetManager()) {
$this->setAssetManager($this->container->get('assetic.asset_manager'));
}
return parent::createAssetReference($name);
}
protected function getFilter($name)
{
if (!$this->getFilterManager()) {
$this->setFilterManager($this->container->get('assetic.filter_manager'));
}
return parent::getFilter($name);
}
}

View File

@ -1,87 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory\Loader;
use Assetic\Factory\Loader\BasePhpFormulaLoader;
/**
* Loads formulae from Symfony2 PHP templates.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AsseticHelperFormulaLoader extends BasePhpFormulaLoader
{
protected function registerPrototypes()
{
return array(
'$view[\'assetic\']->javascripts(*)' => array('output' => 'js/*.js'),
'$view[\'assetic\']->stylesheets(*)' => array('output' => 'css/*.css'),
'$view[\'assetic\']->image(*)' => array('output' => 'images/*', 'single' => true),
'$view["assetic"]->javascripts(*)' => array('output' => 'js/*.js'),
'$view["assetic"]->stylesheets(*)' => array('output' => 'css/*.css'),
'$view["assetic"]->image(*)' => array('output' => 'images/*', 'single' => true),
'$view->get(\'assetic\')->javascripts(*)' => array('output' => 'js/*.js'),
'$view->get(\'assetic\')->stylesheets(*)' => array('output' => 'css/*.css'),
'$view->get(\'assetic\')->image(*)' => array('output' => 'images/*', 'single' => true),
'$view->get("assetic")->javascripts(*)' => array('output' => 'js/*.js'),
'$view->get("assetic")->stylesheets(*)' => array('output' => 'css/*.css'),
'$view->get("assetic")->image(*)' => array('output' => 'images/*', 'single' => true),
);
}
protected function registerSetupCode()
{
return <<<'EOF'
class Helper
{
public function assets()
{
global $_call;
$_call = func_get_args();
}
public function javascripts()
{
global $_call;
$_call = func_get_args();
}
public function stylesheets()
{
global $_call;
$_call = func_get_args();
}
public function image()
{
global $_call;
$_call = func_get_args();
}
}
class View extends ArrayObject
{
public function __construct(Helper $helper)
{
parent::__construct(array('assetic' => $helper));
}
public function get()
{
return $this['assetic'];
}
}
$view = new View(new Helper());
EOF;
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory\Loader;
use Assetic\Factory\Loader\FormulaLoaderInterface;
use Assetic\Factory\Resource\ResourceInterface;
use Symfony\Bundle\AsseticBundle\Factory\ConfigurationResource;
/**
* Loads configured formulae.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class ConfigurationLoader implements FormulaLoaderInterface
{
public function load(ResourceInterface $resource)
{
return $resource instanceof ConfigurationResource ? $resource->getContent() : array();
}
}

View File

@ -1,44 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory\Resource;
use Assetic\Factory\Resource\ResourceInterface;
/**
* A configured resource.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class ConfigurationResource implements ResourceInterface
{
private $formulae;
public function __construct(array $formulae)
{
$this->formulae = $formulae;
}
public function isFresh($timestamp)
{
return true;
}
public function getContent()
{
return $this->formulae;
}
public function __toString()
{
return 'symfony';
}
}

View File

@ -1,51 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory\Resource;
use Assetic\Factory\Resource\DirectoryResource as BaseDirectoryResource;
use Symfony\Component\Templating\Loader\LoaderInterface;
/**
* A directory resource that creates Symfony2 templating resources.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class DirectoryResource extends BaseDirectoryResource
{
protected $loader;
protected $bundle;
protected $path;
/**
* Constructor.
*
* @param LoaderInterface $loader The templating loader
* @param string $bundle The current bundle name
* @param string $path The directory path
* @param string $pattern A regex pattern for file basenames
*/
public function __construct(LoaderInterface $loader, $bundle, $path, $pattern = null)
{
$this->loader = $loader;
$this->bundle = $bundle;
$this->path = rtrim($path, '/').'/';
parent::__construct($path, $pattern);
}
public function getIterator()
{
return is_dir($this->path)
? new DirectoryResourceIterator($this->loader, $this->bundle, $this->path, $this->getInnerIterator())
: new \EmptyIterator();
}
}

View File

@ -1,45 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory\Resource;
use Symfony\Component\Templating\Loader\LoaderInterface;
class DirectoryResourceIterator extends \RecursiveIteratorIterator
{
protected $loader;
protected $bundle;
protected $path;
/**
* Constructor.
*
* @param LoaderInterface $loader The templating loader
* @param string $bundle The current bundle name
* @param string $path The directory
* @param RecursiveIterator $iterator The inner iterator
*/
public function __construct(LoaderInterface $loader, $bundle, $path, \RecursiveIterator $iterator)
{
$this->loader = $loader;
$this->bundle = $bundle;
$this->path = $path;
parent::__construct($iterator);
}
public function current()
{
$file = parent::current();
return new FileResource($this->loader, $this->bundle, $this->path, $file->getPathname());
}
}

View File

@ -1,78 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory\Resource;
use Assetic\Factory\Resource\ResourceInterface;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
use Symfony\Component\Templating\Loader\LoaderInterface;
/**
* A file resource.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class FileResource implements ResourceInterface
{
protected $loader;
protected $bundle;
protected $baseDir;
protected $path;
protected $template;
/**
* Constructor.
*
* @param LoaderInterface $loader The templating loader
* @param string $bundle The current bundle name
* @param string $baseDir The directory
* @param string $path The file path
*/
public function __construct(LoaderInterface $loader, $bundle, $baseDir, $path)
{
$this->loader = $loader;
$this->bundle = $bundle;
$this->baseDir = $baseDir;
$this->path = $path;
}
public function isFresh($timestamp)
{
return $this->loader->isFresh($this->getTemplate(), $timestamp);
}
public function getContent()
{
return $this->loader->load($this->getTemplate())->getContent();
}
public function __toString()
{
return $this->path;
}
protected function getTemplate()
{
if (null === $this->template) {
$this->template = self::createTemplateReference($this->bundle, substr($this->path, strlen($this->baseDir)));
}
return $this->template;
}
static private function createTemplateReference($bundle, $file)
{
$parts = explode('/', strtr($file, '\\', '/'));
$elements = explode('.', array_pop($parts));
return new TemplateReference($bundle, implode('/', $parts), $elements[0], $elements[1], $elements[2]);
}
}

View File

@ -1,33 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Factory\Worker;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\Worker\WorkerInterface;
/**
* Prepends a fake front controller so the asset knows where it is-ish.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class UseControllerWorker implements WorkerInterface
{
public function process(AssetInterface $asset)
{
$targetUrl = $asset->getTargetPath();
if ($targetUrl && '/' != $targetUrl[0] && 0 !== strpos($targetUrl, '_controller/')) {
$asset->setTargetPath('_controller/'.$targetUrl);
}
return $asset;
}
}

View File

@ -1,55 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle;
use Assetic\FilterManager as BaseFilterManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Lazy filter manager.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class FilterManager extends BaseFilterManager
{
protected $container;
protected $mappings;
/**
* Constructor.
*
* @param ContainerInterface $container The service container
* @param array $mappings A hash of filter names to service ids
*/
public function __construct(ContainerInterface $container, array $mappings)
{
$this->container = $container;
$this->mappings = $mappings;
}
public function get($name)
{
return isset($this->mappings[$name])
? $this->container->get($this->mappings[$name])
: parent::get($name);
}
public function has($name)
{
return isset($this->mappings[$name]) || parent::has($name);
}
public function getNames()
{
return array_unique(array_merge(array_keys($this->mappings), parent::getNames()));
}
}

View File

@ -1,31 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Listener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
/**
* Adds a few formats to each request.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class RequestListener
{
public function onCoreRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$request->setFormat('png', 'image/png');
$request->setFormat('jpg', 'image/jpeg');
$request->setFormat('gif', 'image/gif');
}
}

View File

@ -1,22 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.asset_writer_cache_warmer.class">Symfony\Bundle\AsseticBundle\CacheWarmer\AssetWriterCacheWarmer</parameter>
<parameter key="assetic.asset_writer.class">Assetic\AssetWriter</parameter>
</parameters>
<services>
<service id="assetic.asset_writer_cache_warmer" class="%assetic.asset_writer_cache_warmer.class%" public="false">
<tag name="kernel.cache_warmer" />
<argument type="service" id="service_container" />
<argument type="service" id="assetic.asset_writer" />
</service>
<service id="assetic.asset_writer" class="%assetic.asset_writer.class%" public="false">
<argument>%assetic.write_to%</argument>
</service>
</services>
</container>

View File

@ -1,64 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.asset_factory.class">Symfony\Bundle\AsseticBundle\Factory\AssetFactory</parameter>
<parameter key="assetic.asset_manager.class">Assetic\Factory\LazyAssetManager</parameter>
<parameter key="assetic.asset_manager_cache_warmer.class">Symfony\Bundle\AsseticBundle\CacheWarmer\AssetManagerCacheWarmer</parameter>
<parameter key="assetic.cached_formula_loader.class">Assetic\Factory\Loader\CachedFormulaLoader</parameter>
<parameter key="assetic.config_cache.class">Assetic\Cache\ConfigCache</parameter>
<parameter key="assetic.config_loader.class">Symfony\Bundle\AsseticBundle\Factory\Loader\ConfigurationLoader</parameter>
<parameter key="assetic.config_resource.class">Symfony\Bundle\AsseticBundle\Factory\Resource\ConfigurationResource</parameter>
<parameter key="assetic.coalescing_directory_resource.class">Assetic\Factory\Resource\CoalescingDirectoryResource</parameter>
<parameter key="assetic.directory_resource.class">Symfony\Bundle\AsseticBundle\Factory\Resource\DirectoryResource</parameter>
<parameter key="assetic.filter_manager.class">Symfony\Bundle\AsseticBundle\FilterManager</parameter>
<parameter key="assetic.worker.ensure_filter.class">Assetic\Factory\Worker\EnsureFilterWorker</parameter>
<parameter key="assetic.node.paths" type="collection"></parameter>
<parameter key="assetic.cache_dir">%kernel.cache_dir%/assetic</parameter>
</parameters>
<services>
<!-- managers -->
<service id="assetic.filter_manager" class="%assetic.filter_manager.class%">
<argument type="service" id="service_container" />
<argument type="collection"></argument>
</service>
<service id="assetic.asset_manager" class="%assetic.asset_manager.class%">
<argument type="service" id="assetic.asset_factory" />
<argument type="collection"></argument>
</service>
<service id="assetic.asset_factory" class="%assetic.asset_factory.class%" public="false">
<argument type="service" id="kernel" />
<argument type="service" id="service_container" />
<argument>%assetic.read_from%</argument>
<argument>%assetic.debug%</argument>
</service>
<service id="assetic.config_loader" class="%assetic.config_loader.class%" public="false">
<tag name="assetic.formula_loader" alias="config" />
</service>
<service id="assetic.config_resource" class="%assetic.config_resource.class%" public="false">
<tag name="assetic.formula_resource" loader="config" />
<argument type="collection" /> <!-- configured formulae -->
</service>
<service id="assetic.config_cache" class="%assetic.config_cache.class%" public="false">
<argument>%assetic.cache_dir%/config</argument>
</service>
<service id="assetic.asset_manager_cache_warmer" class="%assetic.asset_manager_cache_warmer.class%" public="false">
<tag name="kernel.cache_warmer" priority="10" />
<argument type="service" id="service_container" />
</service>
<service id="assetic.worker.ensure_filter" class="%assetic.worker.ensure_filter.class%" abstract="true" public="false">
<argument /> <!-- pattern -->
<argument /> <!-- filter -->
</service>
</services>
</container>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.controller.class">Symfony\Bundle\AsseticBundle\Controller\AsseticController</parameter>
<parameter key="assetic.routing_loader.class">Symfony\Bundle\AsseticBundle\Routing\AsseticLoader</parameter>
<parameter key="assetic.cache.class">Assetic\Cache\FilesystemCache</parameter>
<parameter key="assetic.use_controller_worker.class">Symfony\Bundle\AsseticBundle\Factory\Worker\UseControllerWorker</parameter>
<parameter key="assetic.request_listener.class">Symfony\Bundle\AsseticBundle\Listener\RequestListener</parameter>
</parameters>
<services>
<service id="assetic.routing_loader" class="%assetic.routing_loader.class%" public="false">
<tag name="routing.loader" />
<argument type="service" id="assetic.asset_manager" />
</service>
<service id="assetic.controller" class="%assetic.controller.class%" scope="prototype">
<argument type="service" id="request" />
<argument type="service" id="assetic.asset_manager" />
<argument type="service" id="assetic.cache" />
</service>
<service id="assetic.cache" class="%assetic.cache.class%" public="false">
<argument>%assetic.cache_dir%/assets</argument>
</service>
<service id="assetic.use_controller_worker" class="%assetic.use_controller_worker.class%" public="false">
<tag name="assetic.factory_worker" />
</service>
<service id="assetic.request_listener" class="%assetic.request_listener.class%">
<tag name="kernel.listener" event="onCoreRequest" />
</service>
</services>
</container>

View File

@ -1,24 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.closure.api.class">Assetic\Filter\GoogleClosure\CompilerApiFilter</parameter>
<parameter key="assetic.filter.closure.jar.class">Assetic\Filter\GoogleClosure\CompilerJarFilter</parameter>
<parameter key="assetic.filter.closure.java">%assetic.java.bin%</parameter>
</parameters>
<services>
<service id="assetic.filter.closure.jar" class="%assetic.filter.closure.jar.class%">
<tag name="assetic.filter" alias="closure" />
<argument>%assetic.filter.closure.jar%</argument>
<argument>%assetic.filter.closure.java%</argument>
</service>
<service id="assetic.filter.closure.api" class="%assetic.filter.closure.api.class%">
<tag name="assetic.filter" alias="closure" />
</service>
</services>
</container>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.coffee.class">Assetic\Filter\CoffeeScriptFilter</parameter>
<parameter key="assetic.filter.coffee.bin">/usr/bin/coffee</parameter>
<parameter key="assetic.filter.coffee.node">%assetic.node.bin%</parameter>
</parameters>
<services>
<service id="assetic.filter.coffee" class="%assetic.filter.coffee.class%">
<tag name="assetic.filter" alias="coffee" />
<argument>%assetic.filter.coffee.bin%</argument>
<argument>%assetic.filter.coffee.node%</argument>
</service>
</services>
</container>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.compass.class">Assetic\Filter\Sass\CompassFilter</parameter>
<parameter key="assetic.filter.compass.sass">%assetic.sass.bin%</parameter>
</parameters>
<services>
<service id="assetic.filter.compass" class="%assetic.filter.compass.class%">
<tag name="assetic.filter" alias="compass" />
<argument>%assetic.filter.compass.sass%</argument>
</service>
</services>
</container>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.cssembed.class">Assetic\Filter\CssEmbedFilter</parameter>
<parameter key="assetic.filter.cssembed.java">%assetic.java.bin%</parameter>
<parameter key="assetic.filter.cssembed.jar" />
</parameters>
<services>
<service id="assetic.filter.cssembed" class="%assetic.filter.cssembed.class%">
<tag name="assetic.filter" alias="cssembed" />
<argument>%assetic.filter.cssembed.jar%</argument>
<argument>%assetic.filter.cssembed.java%</argument>
</service>
</services>
</container>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.cssimport.class">Assetic\Filter\CssImportFilter</parameter>
</parameters>
<services>
<service id="assetic.filter.cssimport" class="%assetic.filter.cssimport.class%">
<tag name="assetic.filter" alias="cssimport" />
</service>
</services>
</container>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.jpegoptim.class">Assetic\Filter\JpegoptimFilter</parameter>
<parameter key="assetic.filter.jpegoptim.bin">/usr/bin/jpegoptim</parameter>
</parameters>
<services>
<service id="assetic.filter.jpegoptim" class="%assetic.filter.jpegoptim.class%">
<tag name="assetic.filter" alias="jpegoptim" />
<argument>%assetic.filter.jpegoptim.bin%</argument>
</service>
</services>
</container>

View File

@ -1,34 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.jpegtran.class">Assetic\Filter\JpegtranFilter</parameter>
<parameter key="assetic.filter.jpegtran.bin">/usr/bin/jpegtran</parameter>
<parameter key="assetic.filter.jpegtran.copy">null</parameter>
<parameter key="assetic.filter.jpegtran.optimize">false</parameter>
<parameter key="assetic.filter.jpegtran.progressive">false</parameter>
<parameter key="assetic.filter.jpegtran.restart">null</parameter>
</parameters>
<services>
<service id="assetic.filter.jpegtran" class="%assetic.filter.jpegtran.class%">
<tag name="assetic.filter" alias="jpegtran" />
<argument>%assetic.filter.jpegtran.bin%</argument>
<call method="setCopy">
<argument>%assetic.filter.jpegtran.copy%</argument>
</call>
<call method="setOptimize">
<argument>%assetic.filter.jpegtran.optimize%</argument>
</call>
<call method="setProgressive">
<argument>%assetic.filter.jpegtran.progressive%</argument>
</call>
<call method="setRestart">
<argument>%assetic.filter.jpegtran.restart%</argument>
</call>
</service>
</services>
</container>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.less.class">Assetic\Filter\LessFilter</parameter>
<parameter key="assetic.filter.less.node">%assetic.node.bin%</parameter>
<parameter key="assetic.filter.less.node_paths">%assetic.node.paths%</parameter>
</parameters>
<services>
<service id="assetic.filter.less" class="%assetic.filter.less.class%">
<tag name="assetic.filter" alias="less" />
<argument>%assetic.filter.less.node%</argument>
<argument>%assetic.filter.less.node_paths%</argument>
</service>
</services>
</container>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.lessphp.class">Assetic\Filter\LessphpFilter</parameter>
</parameters>
<services>
<service id="assetic.filter.lessphp" class="%assetic.filter.lessphp.class%">
<tag name="assetic.filter" alias="lessphp" />
</service>
</services>
</container>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.optipng.class">Assetic\Filter\OptiPngFilter</parameter>
<parameter key="assetic.filter.optipng.bin">/usr/bin/optipng</parameter>
<parameter key="assetic.filter.optipng.level">null</parameter>
</parameters>
<services>
<service id="assetic.filter.optipng" class="%assetic.filter.optipng.class%">
<tag name="assetic.filter" alias="optipng" />
<argument>%assetic.filter.optipng.bin%</argument>
<call method="setLevel">
<argument>%assetic.filter.optipng.level%</argument>
</call>
</service>
</services>
</container>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.packager.class">Assetic\Filter\PackagerFilter</parameter>
<parameter key="assetic.filter.packager.packages" type="collection" />
</parameters>
<services>
<service id="assetic.filter.packager" class="%assetic.filter.packager.class%">
<tag name="assetic.filter" alias="packager" />
<argument>%assetic.filter.packager.packages%</argument>
</service>
</services>
</container>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.pngout.class">Assetic\Filter\PngoutFilter</parameter>
<parameter key="assetic.filter.pngout.bin">/usr/bin/pngout</parameter>
</parameters>
<services>
<service id="assetic.filter.pngout" class="%assetic.filter.pngout.class%">
<tag name="assetic.filter" alias="pngout" />
<argument>%assetic.filter.pngout.bin%</argument>
</service>
</services>
</container>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.sass.class">Assetic\Filter\Sass\SassFilter</parameter>
<parameter key="assetic.filter.sass.bin">%assetic.sass.bin%</parameter>
</parameters>
<services>
<service id="assetic.filter.sass" class="%assetic.filter.sass.class%">
<tag name="assetic.filter" alias="sass" />
<argument>%assetic.filter.sass.bin%</argument>
</service>
</services>
</container>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.scss.class">Assetic\Filter\Sass\ScssFilter</parameter>
<parameter key="assetic.filter.scss.sass">%assetic.sass.bin%</parameter>
</parameters>
<services>
<service id="assetic.filter.scss" class="%assetic.filter.scss.class%">
<tag name="assetic.filter" alias="scss" />
<argument>%assetic.filter.scss.sass%</argument>
</service>
</services>
</container>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.sprockets.class">Assetic\Filter\SprocketsFilter</parameter>
<parameter key="assetic.filter.sprockets.bin">/usr/bin/sprocketize</parameter>
</parameters>
<services>
<service id="assetic.filter.sprockets" class="%assetic.filter.sprockets.class%">
<tag name="assetic.filter" alias="sprockets" />
<argument>%assetic.filter.sprockets.bin%</argument>
</service>
</services>
</container>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.stylus.class">Assetic\Filter\StylusFilter</parameter>
<parameter key="assetic.filter.stylus.node">%assetic.node.bin%</parameter>
<parameter key="assetic.filter.stylus.node_paths">%assetic.node.paths%</parameter>
</parameters>
<services>
<service id="assetic.filter.stylus" class="%assetic.filter.stylus.class%">
<tag name="assetic.filter" alias="stylus" />
<argument>%assetic.filter.stylus.node%</argument>
<argument>%assetic.filter.stylus.node_paths%</argument>
</service>
</services>
</container>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.yui_css.class">Assetic\Filter\Yui\CssCompressorFilter</parameter>
<parameter key="assetic.filter.yui_css.java">%assetic.java.bin%</parameter>
<parameter key="assetic.filter.yui_css.jar" />
</parameters>
<services>
<service id="assetic.filter.yui_css" class="%assetic.filter.yui_css.class%">
<tag name="assetic.filter" alias="yui_css" />
<argument>%assetic.filter.yui_css.jar%</argument>
<argument>%assetic.filter.yui_css.java%</argument>
</service>
</services>
</container>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.filter.yui_js.class">Assetic\Filter\Yui\JsCompressorFilter</parameter>
<parameter key="assetic.filter.yui_js.java">%assetic.java.bin%</parameter>
<parameter key="assetic.filter.yui_js.jar" />
</parameters>
<services>
<service id="assetic.filter.yui_js" class="%assetic.filter.yui_js.class%">
<tag name="assetic.filter" alias="yui_js" />
<argument>%assetic.filter.yui_js.jar%</argument>
<argument>%assetic.filter.yui_js.java%</argument>
</service>
</services>
</container>

View File

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://symfony.com/schema/dic/assetic"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://symfony.com/schema/dic/assetic"
elementFormDefault="qualified">
<xsd:element name="config" type="config" />
<xsd:complexType name="config">
<xsd:sequence>
<xsd:element name="bundle" type="bundle" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="filter" type="filter" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="twig" type="twig" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="debug" type="xsd:string" />
<xsd:attribute name="use-controller" type="xsd:string" />
<xsd:attribute name="read-from" type="xsd:string" />
<xsd:attribute name="write-to" type="xsd:string" />
<xsd:attribute name="closure" type="xsd:string" />
<xsd:attribute name="java" type="xsd:string" />
<xsd:attribute name="node" type="xsd:string" />
<xsd:attribute name="sass" type="xsd:string" />
<xsd:attribute name="yui" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="bundle">
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
<xsd:complexType name="asset">
<xsd:sequence>
<xsd:element name="input" type="xsd:string" minOccurs="1" maxOccurs="unbounded" />
<xsd:element name="filter" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="output" type="xsd:string" />
<xsd:attribute name="debug" type="xsd:string" />
<xsd:anyAttribute namespace="##any" processContents="lax" />
</xsd:complexType>
<xsd:complexType name="filter">
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="resource" type="xsd:string" />
<xsd:anyAttribute namespace="##any" processContents="lax" />
</xsd:complexType>
<xsd:complexType name="twig">
<xsd:sequence>
<xsd:element name="function" type="twig_function" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="twig_function">
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:anyAttribute namespace="##any" processContents="lax" />
</xsd:complexType>
</xsd:schema>

View File

@ -1,39 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.helper.dynamic.class">Symfony\Bundle\AsseticBundle\Templating\DynamicAsseticHelper</parameter>
<parameter key="assetic.helper.static.class">Symfony\Bundle\AsseticBundle\Templating\StaticAsseticHelper</parameter>
<parameter key="assetic.php_formula_loader.class">Symfony\Bundle\AsseticBundle\Factory\Loader\AsseticHelperFormulaLoader</parameter>
</parameters>
<services>
<service id="assetic.helper.dynamic" class="%assetic.helper.dynamic.class%">
<tag name="assetic.templating.php" />
<argument type="service" id="templating.helper.router" />
<argument type="service" id="assetic.asset_factory" />
</service>
<service id="assetic.helper.static" class="%assetic.helper.static.class%">
<tag name="assetic.templating.php" />
<argument type="service" id="templating.helper.assets" />
<argument type="service" id="assetic.asset_factory" />
</service>
<service id="assetic.php_formula_loader" class="%assetic.cached_formula_loader.class%" public="false">
<tag name="assetic.formula_loader" alias="php" />
<tag name="assetic.templating.php" />
<argument type="service" id="assetic.php_formula_loader.real" />
<argument type="service" id="assetic.config_cache" />
<argument>%kernel.debug%</argument>
</service>
<service id="assetic.php_formula_loader.real" class="%assetic.php_formula_loader.class%" public="false">
<tag name="assetic.templating.php" />
<argument type="service" id="assetic.asset_factory" />
</service>
</services>
</container>

View File

@ -1,32 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.twig_extension.class">Symfony\Bundle\AsseticBundle\Twig\AsseticExtension</parameter>
<parameter key="assetic.twig_formula_loader.class">Assetic\Extension\Twig\TwigFormulaLoader</parameter>
</parameters>
<services>
<service id="assetic.twig_extension" class="%assetic.twig_extension.class%" public="false">
<tag name="twig.extension" />
<tag name="assetic.templating.twig" />
<argument type="service" id="assetic.asset_factory" />
<argument>%assetic.use_controller%</argument>
<argument>%assetic.twig_extension.functions%</argument>
</service>
<service id="assetic.twig_formula_loader" class="%assetic.cached_formula_loader.class%" public="false">
<tag name="assetic.formula_loader" alias="twig" />
<tag name="assetic.templating.twig" />
<argument type="service" id="assetic.twig_formula_loader.real" />
<argument type="service" id="assetic.config_cache" />
<argument>%kernel.debug%</argument>
</service>
<service id="assetic.twig_formula_loader.real" class="%assetic.twig_formula_loader.class%" public="false">
<tag name="assetic.templating.twig" />
<argument type="service" id="twig" />
</service>
</services>
</container>

View File

@ -1,123 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Routing;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\LazyAssetManager;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Loads routes for all assets.
*
* Assets should only be served through the routing system for ease-of-use
* during development.
*
* For example, add the following to your application's routing_dev.yml:
*
* _assetic:
* resource: .
* type: assetic
*
* In a production environment you should use the `assetic:dump` command to
* create static asset files.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AsseticLoader extends Loader
{
protected $am;
public function __construct(LazyAssetManager $am)
{
$this->am = $am;
}
public function load($routingResource, $type = null)
{
$routes = new RouteCollection();
// resources
foreach ($this->am->getResources() as $resources) {
if (!$resources instanceof \Traversable) {
$resources = array($resources);
}
foreach ($resources as $resource) {
if (file_exists($path = (string) $resource)) {
$routes->addResource(new FileResource($path));
}
}
}
// routes
foreach ($this->am->getNames() as $name) {
$asset = $this->am->get($name);
$formula = $this->am->getFormula($name);
$this->loadRouteForAsset($routes, $asset, $name);
$debug = isset($formula[2]['debug']) ? $formula[2]['debug'] : $this->am->isDebug();
$combine = isset($formula[2]['combine']) ? $formula[2]['combine'] : !$debug;
// add a route for each "leaf" in debug mode
if (!$combine) {
$i = 0;
foreach ($asset as $leaf) {
$this->loadRouteForAsset($routes, $leaf, $name, $i++);
}
}
}
return $routes;
}
/**
* Loads a route to serve an supplied asset.
*
* The fake front controller that {@link UseControllerWorker} adds to the
* target URL will be removed before set as a route pattern.
*
* @param RouteCollection $routes The route collection
* @param AssetInterface $asset The asset
* @param string $name The name to use
* @param integer $pos The leaf index
*/
private function loadRouteForAsset(RouteCollection $routes, AssetInterface $asset, $name, $pos = null)
{
$defaults = array(
'_controller' => 'assetic.controller:render',
'name' => $name,
'pos' => $pos,
);
// remove the fake front controller
$pattern = str_replace('_controller/', '', $asset->getTargetPath());
if ($format = pathinfo($pattern, PATHINFO_EXTENSION)) {
$defaults['_format'] = $format;
}
$route = '_assetic_'.$name;
if (null !== $pos) {
$route .= '_'.$pos;
}
$routes->add($route, new Route($pattern, $defaults));
}
public function supports($resource, $type = null)
{
return 'assetic' == $type;
}
}

View File

@ -1,157 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Templating;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\AssetFactory;
use Assetic\Util\TraversableString;
use Symfony\Component\Templating\Helper\Helper;
/**
* The "assetic" templating helper.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
abstract class AsseticHelper extends Helper
{
protected $factory;
/**
* Constructor.
*
* @param AssetFactory $factory The asset factory
*/
public function __construct(AssetFactory $factory)
{
$this->factory = $factory;
}
/**
* Returns an array of javascript urls.
*/
public function javascripts($inputs = array(), $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = 'js/*.js';
}
return $this->getAssetUrls($inputs, $filters, $options);
}
/**
* Returns an array of stylesheet urls.
*/
public function stylesheets($inputs = array(), $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = 'css/*.css';
}
return $this->getAssetUrls($inputs, $filters, $options);
}
/**
* Returns an array of one image url.
*/
public function image($inputs = array(), $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = 'images/*';
}
$options['single'] = true;
return $this->getAssetUrls($inputs, $filters, $options);
}
/**
* Gets the URLs for the configured asset.
*
* Usage looks something like this:
*
* <?php foreach ($view['assetic']->assets('@jquery, js/src/core/*', '?yui_js') as $url): ?>
* <script src="<?php echo $url ?>" type="text/javascript"></script>
* <?php endforeach; ?>
*
* When in debug mode, the helper returns an array of one or more URLs.
* When not in debug mode it returns an array of one URL.
*
* @param array|string $inputs An array or comma-separated list of input strings
* @param array|string $filters An array or comma-separated list of filter names
* @param array $options An array of options
*
* @return array An array of URLs for the asset
*/
private function getAssetUrls($inputs = array(), $filters = array(), array $options = array())
{
$explode = function($value)
{
return array_map('trim', explode(',', $value));
};
if (!is_array($inputs)) {
$inputs = $explode($inputs);
}
if (!is_array($filters)) {
$filters = $explode($filters);
}
if (!isset($options['debug'])) {
$options['debug'] = $this->factory->isDebug();
}
if (!isset($options['combine'])) {
$options['combine'] = !$options['debug'];
}
if (isset($options['single']) && $options['single'] && 1 < count($inputs)) {
$inputs = array_slice($inputs, -1);
}
if (!isset($options['name'])) {
$options['name'] = $this->factory->generateAssetName($inputs, $filters, $options);
}
$asset = $this->factory->createAsset($inputs, $filters, $options);
$one = $this->getAssetUrl($asset, $options);
$many = array();
if ($options['combine']) {
$many[] = $one;
} else {
$i = 0;
foreach ($asset as $leaf) {
$many[] = $this->getAssetUrl($leaf, array_replace($options, array(
'name' => $options['name'].'_'.$i++,
)));
}
}
return new TraversableString($one, $many);
}
/**
* Returns an URL for the supplied asset.
*
* @param AssetInterface $asset An asset
* @param array $options An array of options
*
* @return string An echo-ready URL
*/
abstract protected function getAssetUrl(AssetInterface $asset, $options = array());
public function getName()
{
return 'assetic';
}
}

View File

@ -1,44 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Templating;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\AssetFactory;
use Symfony\Bundle\FrameworkBundle\Templating\Helper\RouterHelper;
/**
* The dynamic "assetic" templating helper.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class DynamicAsseticHelper extends AsseticHelper
{
private $routerHelper;
/**
* Constructor.
*
* @param RouterHelper $routerHelper The router helper
* @param AssetFactory $factory The asset factory
*/
public function __construct(RouterHelper $routerHelper, AssetFactory $factory)
{
$this->routerHelper = $routerHelper;
parent::__construct($factory);
}
protected function getAssetUrl(AssetInterface $asset, $options = array())
{
return $this->routerHelper->generate('_assetic_'.$options['name']);
}
}

View File

@ -1,44 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Templating;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\AssetFactory;
use Symfony\Component\Templating\Helper\AssetsHelper;
/**
* The static "assetic" templating helper.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class StaticAsseticHelper extends AsseticHelper
{
private $assetsHelper;
/**
* Constructor.
*
* @param AssetsHelper $assetsHelper The assets helper
* @param AssetFactory $factory The asset factory
*/
public function __construct(AssetsHelper $assetsHelper, AssetFactory $factory)
{
$this->assetsHelper = $assetsHelper;
parent::__construct($factory);
}
protected function getAssetUrl(AssetInterface $asset, $options = array())
{
return $this->assetsHelper->getUrl($asset->getTargetPath(), isset($options['package']) ? $options['package'] : null);
}
}

View File

@ -1,51 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\CacheWarmer;
use Symfony\Bundle\AsseticBundle\CacheWarmer\AssetManagerCacheWarmer;
class AssetManagerCacheWarmerTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
}
public function testWarmUp()
{
$am = $this
->getMockBuilder('Assetic\\Factory\\LazyAssetManager')
->disableOriginalConstructor()
->getMock()
;
$am->expects($this->once())->method('load');
$container = $this
->getMockBuilder('Symfony\\Component\\DependencyInjection\\Container')
->setConstructorArgs(array())
->getMock()
;
$container
->expects($this->once())
->method('get')
->with('assetic.asset_manager')
->will($this->returnValue($am))
;
$warmer = new AssetManagerCacheWarmer($container);
$warmer->warmUp('/path/to/cache');
}
}

View File

@ -1,57 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\CacheWarmer;
use Symfony\Bundle\AsseticBundle\CacheWarmer\AssetWriterCacheWarmer;
class AssetWriterCacheWarmerTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
}
public function testWarmUp()
{
$am = $this->getMock('Assetic\\AssetManager');
$writer = $this
->getMockBuilder('Assetic\\AssetWriter')
->disableOriginalConstructor()
->getMock()
;
$writer
->expects($this->once())
->method('writeManagerAssets')
->with($am)
;
$container = $this
->getMockBuilder('Symfony\\Component\\DependencyInjection\\Container')
->setConstructorArgs(array())
->getMock()
;
$container
->expects($this->once())
->method('get')
->with('assetic.asset_manager')
->will($this->returnValue($am))
;
$warmer = new AssetWriterCacheWarmer($container, $writer);
$warmer->warmUp('/path/to/cache');
}
}

View File

@ -1,169 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\Command;
use Symfony\Bundle\AsseticBundle\Command\DumpCommand;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\NullOutput;
class DumpCommandTest extends \PHPUnit_Framework_TestCase
{
private $writeTo;
private $application;
private $definition;
private $kernel;
private $container;
private $am;
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
$this->writeTo = sys_get_temp_dir().'/assetic_dump';
$this->application = $this->getMockBuilder('Symfony\\Bundle\\FrameworkBundle\\Console\\Application')
->disableOriginalConstructor()
->getMock();
$this->definition = $this->getMockBuilder('Symfony\\Component\\Console\\Input\\InputDefinition')
->disableOriginalConstructor()
->getMock();
$this->kernel = $this->getMock('Symfony\\Component\\HttpKernel\\KernelInterface');
$this->container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerInterface');
$this->am = $this->getMockBuilder('Assetic\\Factory\\LazyAssetManager')
->disableOriginalConstructor()
->getMock();
$this->application->expects($this->any())
->method('getDefinition')
->will($this->returnValue($this->definition));
$this->definition->expects($this->any())
->method('getArguments')
->will($this->returnValue(array()));
$this->definition->expects($this->any())
->method('getOptions')
->will($this->returnValue(array(
new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'The Environment name.', 'dev'),
new InputOption('--no-debug', null, InputOption::VALUE_NONE, 'Switches off debug mode.'),
)));
$this->application->expects($this->any())
->method('getKernel')
->will($this->returnValue($this->kernel));
$this->kernel->expects($this->any())
->method('getContainer')
->will($this->returnValue($this->container));
$this->container->expects($this->any())
->method('getParameter')
->with('assetic.write_to')
->will($this->returnValue($this->writeTo));
$this->container->expects($this->once())
->method('get')
->with('assetic.asset_manager')
->will($this->returnValue($this->am));
$this->command = new DumpCommand();
$this->command->setApplication($this->application);
}
protected function tearDown()
{
if (is_dir($this->writeTo)) {
array_map('unlink', glob($this->writeTo.'/*'));
rmdir($this->writeTo);
}
}
public function testEmptyAssetManager()
{
$this->am->expects($this->once())
->method('getNames')
->will($this->returnValue(array()));
$this->command->run(new ArrayInput(array()), new NullOutput());
}
public function testDumpOne()
{
$asset = $this->getMock('Assetic\\Asset\\AssetInterface');
$this->am->expects($this->once())
->method('getNames')
->will($this->returnValue(array('test_asset')));
$this->am->expects($this->once())
->method('get')
->with('test_asset')
->will($this->returnValue($asset));
$this->am->expects($this->once())
->method('getFormula')
->with('test_asset')
->will($this->returnValue(array()));
$this->am->expects($this->once())
->method('isDebug')
->will($this->returnValue(false));
$asset->expects($this->once())
->method('getTargetPath')
->will($this->returnValue('test_asset.css'));
$asset->expects($this->once())
->method('dump')
->will($this->returnValue('/* test_asset */'));
$this->command->run(new ArrayInput(array()), new NullOutput());
$this->assertFileExists($this->writeTo.'/test_asset.css');
$this->assertEquals('/* test_asset */', file_get_contents($this->writeTo.'/test_asset.css'));
}
public function testDumpDebug()
{
$asset = $this->getMock('Assetic\\Asset\\AssetCollection');
$leaf = $this->getMock('Assetic\\Asset\\AssetInterface');
$this->am->expects($this->once())
->method('getNames')
->will($this->returnValue(array('test_asset')));
$this->am->expects($this->once())
->method('get')
->with('test_asset')
->will($this->returnValue($asset));
$this->am->expects($this->once())
->method('getFormula')
->with('test_asset')
->will($this->returnValue(array()));
$this->am->expects($this->once())
->method('isDebug')
->will($this->returnValue(true));
$asset->expects($this->once())
->method('getTargetPath')
->will($this->returnValue('test_asset.css'));
$asset->expects($this->once())
->method('dump')
->will($this->returnValue('/* test_asset */'));
$asset->expects($this->once())
->method('getIterator')
->will($this->returnValue(new \ArrayIterator(array($leaf))));
$leaf->expects($this->once())
->method('getTargetPath')
->will($this->returnValue('test_leaf.css'));
$leaf->expects($this->once())
->method('dump')
->will($this->returnValue('/* test_leaf */'));
$this->command->run(new ArrayInput(array()), new NullOutput());
$this->assertFileExists($this->writeTo.'/test_asset.css');
$this->assertFileExists($this->writeTo.'/test_leaf.css');
$this->assertEquals('/* test_asset */', file_get_contents($this->writeTo.'/test_asset.css'));
$this->assertEquals('/* test_leaf */', file_get_contents($this->writeTo.'/test_leaf.css'));
}
}

View File

@ -1,167 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\Controller;
use Symfony\Bundle\AsseticBundle\Controller\AsseticController;
class AsseticControllerTest extends \PHPUnit_Framework_TestCase
{
protected $request;
protected $headers;
protected $am;
protected $cache;
protected $controller;
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
$this->request = $this->getMock('Symfony\\Component\\HttpFoundation\\Request');
$this->headers = $this->getMock('Symfony\\Component\\HttpFoundation\\ParameterBag');
$this->request->headers = $this->headers;
$this->am = $this->getMockBuilder('Assetic\\Factory\\LazyAssetManager')
->disableOriginalConstructor()
->getMock();
$this->cache = $this->getMock('Assetic\\Cache\\CacheInterface');
$this->controller = new AsseticController($this->request, $this->am, $this->cache);
}
public function testRenderNotFound()
{
$this->setExpectedException('Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException');
$name = 'foo';
$this->am->expects($this->once())
->method('has')
->with($name)
->will($this->returnValue(false));
$this->controller->render($name);
}
public function testRenderLastModifiedFresh()
{
$asset = $this->getMock('Assetic\\Asset\\AssetInterface');
$name = 'foo';
$lastModified = strtotime('2010-10-10 10:10:10');
$ifModifiedSince = gmdate('D, d M Y H:i:s', $lastModified).' GMT';
$asset->expects($this->any())->method('getFilters')->will($this->returnValue(array()));
$this->am->expects($this->once())->method('has')->with($name)->will($this->returnValue(true));
$this->am->expects($this->once())->method('get')->with($name)->will($this->returnValue($asset));
$asset->expects($this->once())->method('getLastModified')->will($this->returnValue($lastModified));
$this->headers->expects($this->once())->method('get')->with('If-Modified-Since')->will($this->returnValue($ifModifiedSince));
$asset->expects($this->never())
->method('dump');
$response = $this->controller->render($name);
$this->assertEquals(304, $response->getStatusCode(), '->render() sends a Not Modified response when If-Modified-Since is fresh');
}
public function testRenderLastModifiedStale()
{
$asset = $this->getMock('Assetic\\Asset\\AssetInterface');
$name = 'foo';
$content = '==ASSET_CONTENT==';
$lastModified = strtotime('2010-10-10 10:10:10');
$ifModifiedSince = gmdate('D, d M Y H:i:s', $lastModified - 300).' GMT';
$asset->expects($this->any())->method('getFilters')->will($this->returnValue(array()));
$this->am->expects($this->once())->method('has')->with($name)->will($this->returnValue(true));
$this->am->expects($this->once())->method('get')->with($name)->will($this->returnValue($asset));
$asset->expects($this->exactly(2))->method('getLastModified')->will($this->returnValue($lastModified));
$this->headers->expects($this->once())->method('get')->with('If-Modified-Since')->will($this->returnValue($ifModifiedSince));
$this->cache->expects($this->once())
->method('has')
->with($this->isType('string'))
->will($this->returnValue(false));
$asset->expects($this->once())
->method('dump')
->will($this->returnValue($content));
$response = $this->controller->render($name);
$this->assertEquals(200, $response->getStatusCode(), '->render() sends an OK response when If-Modified-Since is stale');
$this->assertEquals($content, $response->getContent(), '->render() sends the dumped asset as the response content');
}
public function testRenderETagFresh()
{
$asset = $this->getMock('Assetic\\Asset\\AssetInterface');
$name = 'foo';
$formula = array(array('js/core.js'), array(), array(''));
$etag = md5(serialize($formula + array('last_modified' => null)));
$asset->expects($this->any())->method('getFilters')->will($this->returnValue(array()));
$this->am->expects($this->once())->method('has')->with($name)->will($this->returnValue(true));
$this->am->expects($this->once())->method('get')->with($name)->will($this->returnValue($asset));
$this->am->expects($this->once())
->method('hasFormula')
->with($name)
->will($this->returnValue(true));
$this->am->expects($this->once())
->method('getFormula')
->with($name)
->will($this->returnValue($formula));
$this->request->expects($this->once())
->method('getETags')
->will($this->returnValue(array('"'.$etag.'"')));
$asset->expects($this->never())
->method('dump');
$response = $this->controller->render($name);
$this->assertEquals(304, $response->getStatusCode(), '->render() sends a Not Modified response when If-None-Match is fresh');
}
public function testRenderETagStale()
{
$asset = $this->getMock('Assetic\\Asset\\AssetInterface');
$name = 'foo';
$content = '==ASSET_CONTENT==';
$formula = array(array('js/core.js'), array(), array(''));
$etag = md5(serialize($formula + array('last_modified' => null)));
$asset->expects($this->any())->method('getFilters')->will($this->returnValue(array()));
$this->am->expects($this->once())->method('has')->with($name)->will($this->returnValue(true));
$this->am->expects($this->once())->method('get')->with($name)->will($this->returnValue($asset));
$this->am->expects($this->once())
->method('hasFormula')
->with($name)
->will($this->returnValue(true));
$this->am->expects($this->once())
->method('getFormula')
->with($name)
->will($this->returnValue($formula));
$this->request->expects($this->once())
->method('getETags')
->will($this->returnValue(array('"123"')));
$asset->expects($this->once())
->method('dump')
->will($this->returnValue($content));
$response = $this->controller->render($name);
$this->assertEquals(200, $response->getStatusCode(), '->render() sends an OK response when If-None-Match is stale');
$this->assertEquals($content, $response->getContent(), '->render() sends the dumped asset as the response content');
}
}

View File

@ -1,213 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\DependencyInjection;
use Symfony\Bundle\AsseticBundle\DependencyInjection\AsseticExtension;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\CheckYuiFilterPass;
use Symfony\Bundle\AsseticBundle\DependencyInjection\Compiler\CheckClosureFilterPass;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Scope;
use Symfony\Component\HttpFoundation\Request;
class AsseticExtensionTest extends \PHPUnit_Framework_TestCase
{
private $kernel;
private $container;
static public function assertSaneContainer(Container $container, $message = '')
{
$errors = array();
foreach ($container->getServiceIds() as $id) {
try {
$container->get($id);
} catch (\Exception $e) {
$errors[$id] = $e->getMessage();
}
}
self::assertEquals(array(), $errors, $message);
}
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
$this->kernel = $this->getMock('Symfony\\Component\\HttpKernel\\KernelInterface');
$this->container = new ContainerBuilder();
$this->container->addScope(new Scope('request'));
$this->container->register('request', 'Symfony\\Component\\HttpFoundation\\Request')->setScope('request');
$this->container->register('templating.helper.assets', $this->getMockClass('Symfony\\Component\\Templating\\Helper\\AssetsHelper'));
$this->container->register('templating.helper.router', $this->getMockClass('Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\RouterHelper'))
->addArgument(new Definition($this->getMockClass('Symfony\\Component\\Routing\\RouterInterface')));
$this->container->register('twig', 'Twig_Environment');
$this->container->setParameter('kernel.bundles', array());
$this->container->setParameter('kernel.cache_dir', __DIR__);
$this->container->setParameter('kernel.debug', false);
$this->container->setParameter('kernel.root_dir', __DIR__);
$this->container->set('kernel', $this->kernel);
}
/**
* @dataProvider getDebugModes
*/
public function testDefaultConfig($debug)
{
$this->container->setParameter('kernel.debug', $debug);
$extension = new AsseticExtension();
$extension->load(array(array()), $this->container);
$this->assertFalse($this->container->has('assetic.filter.yui_css'), '->load() does not load the yui_css filter when a yui value is not provided');
$this->assertFalse($this->container->has('assetic.filter.yui_js'), '->load() does not load the yui_js filter when a yui value is not provided');
$this->assertSaneContainer($this->getDumpedContainer());
}
public function getDebugModes()
{
return array(
array(true),
array(false),
);
}
/**
* @dataProvider getFilterNames
*/
public function testFilterConfigs($name, $config = array())
{
$extension = new AsseticExtension();
$extension->load(array(array('filters' => array($name => $config))), $this->container);
$this->assertSaneContainer($this->getDumpedContainer());
}
public function getFilterNames()
{
return array(
array('closure', array('jar' => '/path/to/closure.jar')),
array('coffee'),
array('compass'),
array('cssembed', array('jar' => '/path/to/cssembed.jar')),
array('cssimport'),
array('cssrewrite'),
array('jpegoptim'),
array('jpegtran'),
array('less'),
array('lessphp'),
array('optipng'),
array('packager'),
array('pngout'),
array('sass'),
array('scss'),
array('sprockets'),
array('stylus'),
array('yui_css', array('jar' => '/path/to/yuicompressor.jar')),
array('yui_js', array('jar' => '/path/to/yuicompressor.jar')),
);
}
/**
* @dataProvider getUseControllerKeys
*/
public function testUseController($bool, $includes, $omits)
{
$extension = new AsseticExtension();
$extension->load(array(array('use_controller' => $bool)), $this->container);
foreach ($includes as $id) {
$this->assertTrue($this->container->has($id), '"'.$id.'" is registered when use_controller is '.$bool);
}
foreach ($omits as $id) {
$this->assertFalse($this->container->has($id), '"'.$id.'" is not registered when use_controller is '.$bool);
}
$this->assertSaneContainer($this->getDumpedContainer());
}
public function getUseControllerKeys()
{
return array(
array(true, array('assetic.routing_loader', 'assetic.controller'), array('assetic.asset_writer_cache_warmer', 'assetic.asset_writer')),
array(false, array('assetic.asset_writer_cache_warmer', 'assetic.asset_writer'), array('assetic.routing_loader', 'assetic.controller')),
);
}
/**
* @dataProvider getClosureJarAndExpected
*/
public function testClosureCompilerPass($jar, $expected)
{
$this->container->addCompilerPass(new CheckClosureFilterPass());
$extension = new AsseticExtension();
$extension->load(array(array(
'filters' => array(
'closure' => array('jar' => $jar),
),
)), $this->container);
$container = $this->getDumpedContainer();
$this->assertSaneContainer($container);
$this->assertTrue($this->container->getDefinition($expected)->hasTag('assetic.filter'));
$this->assertNotEmpty($container->getParameter('assetic.filter.closure.java'));
}
public function getClosureJarAndExpected()
{
return array(
array(null, 'assetic.filter.closure.api'),
array('/path/to/closure.jar', 'assetic.filter.closure.jar'),
);
}
public function testInvalidYuiConfig()
{
$this->setExpectedException('RuntimeException', 'assetic.filters.yui_js');
$this->container->addCompilerPass(new CheckYuiFilterPass());
$extension = new AsseticExtension();
$extension->load(array(array(
'filters' => array(
'yui_js' => array(),
),
)), $this->container);
$this->getDumpedContainer();
}
private function getDumpedContainer()
{
static $i = 0;
$class = 'AsseticExtensionTestContainer'.$i++;
$this->container->compile();
$dumper = new PhpDumper($this->container);
eval('?>'.$dumper->dump(array('class' => $class)));
$container = new $class();
$container->enterScope('request');
$container->set('kernel', $this->kernel);
return $container;
}
}

View File

@ -1,90 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests\Factory;
use Symfony\Bundle\AsseticBundle\Factory\AssetFactory;
class AssetFactoryTest extends \PHPUnit_Framework_TestCase
{
protected $kernel;
protected $factory;
protected $container;
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
$this->kernel = $this->getMock('Symfony\\Component\\HttpKernel\\KernelInterface');
$this->container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerInterface');
$this->factory = new AssetFactory($this->kernel, $this->container, '/path/to/web');
}
public function testBundleNotation()
{
$input = '@MyBundle/Resources/css/main.css';
$bundle = $this->getMock('Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface');
$this->kernel->expects($this->once())
->method('getBundle')
->with('MyBundle')
->will($this->returnValue($bundle));
$this->kernel->expects($this->once())
->method('locateResource')
->with($input)
->will($this->returnValue('/path/to/MyBundle/Resources/css/main.css'));
$bundle->expects($this->once())
->method('getPath')
->will($this->returnValue('/path/to/MyBundle'));
$coll = $this->factory->createAsset($input)->all();
$asset = $coll[0];
$this->assertEquals('/path/to/MyBundle', $asset->getSourceRoot(), '->createAsset() sets the asset root');
$this->assertEquals('Resources/css/main.css', $asset->getSourcePath(), '->createAsset() sets the asset path');
}
/**
* @dataProvider getGlobs
*/
public function testBundleGlobNotation($input)
{
$bundle = $this->getMock('Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface');
$this->kernel->expects($this->once())
->method('getBundle')
->with('MyBundle')
->will($this->returnValue($bundle));
$this->kernel->expects($this->once())
->method('locateResource')
->with('@MyBundle/Resources/css/')
->will($this->returnValue('/path/to/MyBundle/Resources/css/'));
$bundle->expects($this->once())
->method('getPath')
->will($this->returnValue('/path/to/MyBundle'));
$coll = $this->factory->createAsset($input)->all();
$asset = $coll[0];
$this->assertEquals('/path/to/MyBundle', $asset->getSourceRoot(), '->createAsset() sets the asset root');
$this->assertNull($asset->getSourcePath(), '->createAsset() sets the asset path to null');
}
public function getGlobs()
{
return array(
array('@MyBundle/Resources/css/*'),
array('@MyBundle/Resources/css/*/*.css'),
);
}
}

View File

@ -1,59 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests;
use Symfony\Bundle\AsseticBundle\FilterManager;
class FilterManagerTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
}
public function testGet()
{
$container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerInterface');
$filter = $this->getMock('Assetic\\Filter\\FilterInterface');
$container->expects($this->exactly(2))
->method('get')
->with('assetic.filter.bar')
->will($this->returnValue($filter));
$fm = new FilterManager($container, array('foo' => 'assetic.filter.bar'));
$this->assertSame($filter, $fm->get('foo'), '->get() loads the filter from the container');
$this->assertSame($filter, $fm->get('foo'), '->get() loads the filter from the container');
}
public function testHas()
{
$container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerInterface');
$fm = new FilterManager($container, array('foo' => 'assetic.filter.bar'));
$this->assertTrue($fm->has('foo'), '->has() returns true for lazily mapped filters');
}
public function testGetNames()
{
$container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerInterface');
$filter = $this->getMock('Assetic\\Filter\\FilterInterface');
$fm = new FilterManager($container, array('foo' => 'assetic.filter.bar'));
$fm->set('bar', $filter);
$this->assertEquals(array('foo', 'bar'), $fm->getNames(), '->getNames() returns all lazy and normal filter names');
}
}

View File

@ -1,75 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Tests;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Util\Filesystem;
/**
* @group functional
*/
class FunctionalTest extends \PHPUnit_Framework_TestCase
{
protected $cacheDir;
protected function setUp()
{
if (!class_exists('Assetic\\AssetManager')) {
$this->markTestSkipped('Assetic is not available.');
}
$this->cacheDir = __DIR__.'/Resources/cache';
if (file_exists($this->cacheDir)) {
$filesystem = new Filesystem();
$filesystem->remove($this->cacheDir);
}
mkdir($this->cacheDir, 0777, true);
}
protected function tearDown()
{
$filesystem = new Filesystem();
$filesystem->remove($this->cacheDir);
}
public function testTwigRenderDebug()
{
$kernel = new TestKernel('test', true);
$kernel->boot();
$container = $kernel->getContainer();
$container->enterScope('request');
$container->set('request', new Request());
$content = $container->get('templating')->render('::layout.html.twig');
$crawler = new Crawler($content);
$this->assertEquals(3, count($crawler->filter('link[href$=".css"]')));
$this->assertEquals(2, count($crawler->filter('script[src$=".js"]')));
}
public function testPhpRenderDebug()
{
$kernel = new TestKernel('test', true);
$kernel->boot();
$container = $kernel->getContainer();
$container->enterScope('request');
$container->set('request', new Request());
$content = $container->get('templating')->render('::layout.html.php');
$crawler = new Crawler($content);
$this->assertEquals(3, count($crawler->filter('link[href$=".css"]')));
$this->assertEquals(2, count($crawler->filter('script[src$=".js"]')));
}
}

View File

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title><?php $view['slots']->output('title') ?></title>
<?php $view['slots']->output('stylesheets') ?>
</head>
<body>
<?php $view['slots']->output('_content') ?>
<?php $view['slots']->output('javascripts') ?>
</body>
</html>

View File

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>{% block title '' %}</title>
{% block stylesheets '' %}
</head>
<body>
{% block content '' %}
{% block javascripts '' %}
</body>
</html>

View File

@ -1,17 +0,0 @@
<?php $view->extend('::base.html.php') ?>
<?php $view['slots']->start('stylesheets') ?>
<?php foreach($view['assetic']->stylesheets('stylesheet1.css, stylesheet2.css, @TestBundle/Resources/css/bundle.css') as $url): ?>
<link href="<?php echo $view->escape($url) ?>" type="text/css" rel="stylesheet" />
<?php endforeach; ?>
<?php $view['slots']->stop() ?>
<?php $view['slots']->start('javascripts') ?>
<?php foreach($view['assetic']->javascripts('javascript1.js, javascript2.js') as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach; ?>
<?php $view['slots']->stop() ?>
<?php foreach ($view['assetic']->image('logo.png') as $url): ?>
<img src="<?php echo $view->escape($url) ?>">
<?php endforeach; ?>

Some files were not shown because too many files have changed in this diff Show More