diff --git a/src/Symfony/Components/File/Exception/AccessDeniedException.php b/src/Symfony/Components/File/Exception/AccessDeniedException.php index eb8a3a4d7b..d60a28cf68 100644 --- a/src/Symfony/Components/File/Exception/AccessDeniedException.php +++ b/src/Symfony/Components/File/Exception/AccessDeniedException.php @@ -17,13 +17,13 @@ namespace Symfony\Components\File\Exception; */ class AccessDeniedException extends FileException { - /** - * Constructor. - * - * @param string $path The path to the accessed file - */ - public function __construct($path) - { - parent::__construct(sprintf('The file %s could not be accessed', $path)); - } + /** + * Constructor. + * + * @param string $path The path to the accessed file + */ + public function __construct($path) + { + parent::__construct(sprintf('The file %s could not be accessed', $path)); + } } \ No newline at end of file diff --git a/src/Symfony/Components/File/Exception/FileNotFoundException.php b/src/Symfony/Components/File/Exception/FileNotFoundException.php index b5cfd1c92c..4118eacf41 100644 --- a/src/Symfony/Components/File/Exception/FileNotFoundException.php +++ b/src/Symfony/Components/File/Exception/FileNotFoundException.php @@ -17,13 +17,13 @@ namespace Symfony\Components\File\Exception; */ class FileNotFoundException extends FileException { - /** - * Constructor. - * - * @param string $path The path to the file that was not found - */ - public function __construct($path) - { - parent::__construct(sprintf('The file %s does not exist', $path)); - } + /** + * Constructor. + * + * @param string $path The path to the file that was not found + */ + public function __construct($path) + { + parent::__construct(sprintf('The file %s does not exist', $path)); + } } \ No newline at end of file diff --git a/src/Symfony/Components/File/File.php b/src/Symfony/Components/File/File.php index 719c33966e..3b10b76e25 100644 --- a/src/Symfony/Components/File/File.php +++ b/src/Symfony/Components/File/File.php @@ -21,566 +21,557 @@ use Symfony\Components\File\MimeType\MimeTypeGuesser; */ class File { - /** - * Assignment of mime types to their default extensions - * @var array - */ - static protected $defaultExtensions = array( - 'application/andrew-inset' => 'ez', - 'application/appledouble' => 'base64', - 'application/applefile' => 'base64', - 'application/commonground' => 'dp', - 'application/cprplayer' => 'pqi', - 'application/dsptype' => 'tsp', - 'application/excel' => 'xls', - 'application/font-tdpfr' => 'pfr', - 'application/futuresplash' => 'spl', - 'application/hstu' => 'stk', - 'application/hyperstudio' => 'stk', - 'application/javascript' => 'js', - 'application/mac-binhex40' => 'hqx', - 'application/mac-compactpro' => 'cpt', - 'application/mbed' => 'mbd', - 'application/mirage' => 'mfp', - 'application/msword' => 'doc', - 'application/ocsp-request' => 'orq', - 'application/ocsp-response' => 'ors', - 'application/octet-stream' => 'bin', - 'application/oda' => 'oda', - 'application/ogg' => 'ogg', - 'application/pdf' => 'pdf', - 'application/x-pdf' => 'pdf', - 'application/pgp-encrypted' => '7bit', - 'application/pgp-keys' => '7bit', - 'application/pgp-signature' => 'sig', - 'application/pkcs10' => 'p10', - 'application/pkcs7-mime' => 'p7m', - 'application/pkcs7-signature' => 'p7s', - 'application/pkix-cert' => 'cer', - 'application/pkix-crl' => 'crl', - 'application/pkix-pkipath' => 'pkipath', - 'application/pkixcmp' => 'pki', - 'application/postscript' => 'ps', - 'application/presentations' => 'shw', - 'application/prs.cww' => 'cw', - 'application/prs.nprend' => 'rnd', - 'application/quest' => 'qrt', - 'application/rtf' => 'rtf', - 'application/sgml-open-catalog' => 'soc', - 'application/sieve' => 'siv', - 'application/smil' => 'smi', - 'application/toolbook' => 'tbk', - 'application/vnd.3gpp.pic-bw-large' => 'plb', - 'application/vnd.3gpp.pic-bw-small' => 'psb', - 'application/vnd.3gpp.pic-bw-var' => 'pvb', - 'application/vnd.3gpp.sms' => 'sms', - 'application/vnd.acucorp' => 'atc', - 'application/vnd.adobe.xfdf' => 'xfdf', - 'application/vnd.amiga.amu' => 'ami', - 'application/vnd.blueice.multipass' => 'mpm', - 'application/vnd.cinderella' => 'cdy', - 'application/vnd.cosmocaller' => 'cmc', - 'application/vnd.criticaltools.wbs+xml' => 'wbs', - 'application/vnd.curl' => 'curl', - 'application/vnd.data-vision.rdz' => 'rdz', - 'application/vnd.dreamfactory' => 'dfac', - 'application/vnd.fsc.weblauch' => 'fsc', - 'application/vnd.genomatix.tuxedo' => 'txd', - 'application/vnd.hbci' => 'hbci', - 'application/vnd.hhe.lesson-player' => 'les', - 'application/vnd.hp-hpgl' => 'plt', - 'application/vnd.ibm.electronic-media' => 'emm', - 'application/vnd.ibm.rights-management' => 'irm', - 'application/vnd.ibm.secure-container' => 'sc', - 'application/vnd.ipunplugged.rcprofile' => 'rcprofile', - 'application/vnd.irepository.package+xml' => 'irp', - 'application/vnd.jisp' => 'jisp', - 'application/vnd.kde.karbon' => 'karbon', - 'application/vnd.kde.kchart' => 'chrt', - 'application/vnd.kde.kformula' => 'kfo', - 'application/vnd.kde.kivio' => 'flw', - 'application/vnd.kde.kontour' => 'kon', - 'application/vnd.kde.kpresenter' => 'kpr', - 'application/vnd.kde.kspread' => 'ksp', - 'application/vnd.kde.kword' => 'kwd', - 'application/vnd.kenameapp' => 'htke', - 'application/vnd.kidspiration' => 'kia', - 'application/vnd.kinar' => 'kne', - 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd', - 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe', - 'application/vnd.lotus-1-2-3' => 'wks', - 'application/vnd.mcd' => 'mcd', - 'application/vnd.mfmp' => 'mfm', - 'application/vnd.micrografx.flo' => 'flo', - 'application/vnd.micrografx.igx' => 'igx', - 'application/vnd.mif' => 'mif', - 'application/vnd.mophun.application' => 'mpn', - 'application/vnd.mophun.certificate' => 'mpc', - 'application/vnd.mozilla.xul+xml' => 'xul', - 'application/vnd.ms-artgalry' => 'cil', - 'application/vnd.ms-asf' => 'asf', - 'application/vnd.ms-excel' => 'xls', - 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm', - 'application/vnd.ms-lrm' => 'lrm', - 'application/vnd.ms-powerpoint' => 'ppt', - 'application/vnd.ms-project' => 'mpp', - 'application/vnd.ms-tnef' => 'base64', - 'application/vnd.ms-works' => 'base64', - 'application/vnd.ms-wpl' => 'wpl', - 'application/vnd.mseq' => 'mseq', - 'application/vnd.nervana' => 'ent', - 'application/vnd.nokia.radio-preset' => 'rpst', - 'application/vnd.nokia.radio-presets' => 'rpss', - 'application/vnd.oasis.opendocument.text' => 'odt', - 'application/vnd.oasis.opendocument.text-template' => 'ott', - 'application/vnd.oasis.opendocument.text-web' => 'oth', - 'application/vnd.oasis.opendocument.text-master' => 'odm', - 'application/vnd.oasis.opendocument.graphics' => 'odg', - 'application/vnd.oasis.opendocument.graphics-template' => 'otg', - 'application/vnd.oasis.opendocument.presentation' => 'odp', - 'application/vnd.oasis.opendocument.presentation-template' => 'otp', - 'application/vnd.oasis.opendocument.spreadsheet' => 'ods', - 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots', - 'application/vnd.oasis.opendocument.chart' => 'odc', - 'application/vnd.oasis.opendocument.formula' => 'odf', - 'application/vnd.oasis.opendocument.database' => 'odb', - 'application/vnd.oasis.opendocument.image' => 'odi', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx', - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', - 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx', - 'application/vnd.palm' => 'prc', - 'application/vnd.picsel' => 'efif', - 'application/vnd.pvi.ptid1' => 'pti', - 'application/vnd.quark.quarkxpress' => 'qxd', - 'application/vnd.sealed.doc' => 'sdoc', - 'application/vnd.sealed.eml' => 'seml', - 'application/vnd.sealed.mht' => 'smht', - 'application/vnd.sealed.ppt' => 'sppt', - 'application/vnd.sealed.xls' => 'sxls', - 'application/vnd.sealedmedia.softseal.html' => 'stml', - 'application/vnd.sealedmedia.softseal.pdf' => 'spdf', - 'application/vnd.seemail' => 'see', - 'application/vnd.smaf' => 'mmf', - 'application/vnd.sun.xml.calc' => 'sxc', - 'application/vnd.sun.xml.calc.template' => 'stc', - 'application/vnd.sun.xml.draw' => 'sxd', - 'application/vnd.sun.xml.draw.template' => 'std', - 'application/vnd.sun.xml.impress' => 'sxi', - 'application/vnd.sun.xml.impress.template' => 'sti', - 'application/vnd.sun.xml.math' => 'sxm', - 'application/vnd.sun.xml.writer' => 'sxw', - 'application/vnd.sun.xml.writer.global' => 'sxg', - 'application/vnd.sun.xml.writer.template' => 'stw', - 'application/vnd.sus-calendar' => 'sus', - 'application/vnd.vidsoft.vidconference' => 'vsc', - 'application/vnd.visio' => 'vsd', - 'application/vnd.visionary' => 'vis', - 'application/vnd.wap.sic' => 'sic', - 'application/vnd.wap.slc' => 'slc', - 'application/vnd.wap.wbxml' => 'wbxml', - 'application/vnd.wap.wmlc' => 'wmlc', - 'application/vnd.wap.wmlscriptc' => 'wmlsc', - 'application/vnd.webturbo' => 'wtb', - 'application/vnd.wordperfect' => 'wpd', - 'application/vnd.wqd' => 'wqd', - 'application/vnd.wv.csp+wbxml' => 'wv', - 'application/vnd.wv.csp+xml' => '8bit', - 'application/vnd.wv.ssp+xml' => '8bit', - 'application/vnd.yamaha.hv-dic' => 'hvd', - 'application/vnd.yamaha.hv-script' => 'hvs', - 'application/vnd.yamaha.hv-voice' => 'hvp', - 'application/vnd.yamaha.smaf-audio' => 'saf', - 'application/vnd.yamaha.smaf-phrase' => 'spf', - 'application/vocaltec-media-desc' => 'vmd', - 'application/vocaltec-media-file' => 'vmf', - 'application/vocaltec-talker' => 'vtk', - 'application/watcherinfo+xml' => 'wif', - 'application/wordperfect5.1' => 'wp5', - 'application/x-123' => 'wk', - 'application/x-7th_level_event' => '7ls', - 'application/x-authorware-bin' => 'aab', - 'application/x-authorware-map' => 'aam', - 'application/x-authorware-seg' => 'aas', - 'application/x-bcpio' => 'bcpio', - 'application/x-bleeper' => 'bleep', - 'application/x-bzip2' => 'bz2', - 'application/x-cdlink' => 'vcd', - 'application/x-chat' => 'chat', - 'application/x-chess-pgn' => 'pgn', - 'application/x-compress' => 'z', - 'application/x-cpio' => 'cpio', - 'application/x-cprplayer' => 'pqf', - 'application/x-csh' => 'csh', - 'application/x-cu-seeme' => 'csm', - 'application/x-cult3d-object' => 'co', - 'application/x-debian-package' => 'deb', - 'application/x-director' => 'dcr', - 'application/x-dvi' => 'dvi', - 'application/x-envoy' => 'evy', - 'application/x-futuresplash' => 'spl', - 'application/x-gtar' => 'gtar', - 'application/x-gzip' => 'gz', - 'application/x-hdf' => 'hdf', - 'application/x-hep' => 'hep', - 'application/x-html+ruby' => 'rhtml', - 'application/x-httpd-miva' => 'mv', - 'application/x-httpd-php' => 'phtml', - 'application/x-ica' => 'ica', - 'application/x-imagemap' => 'imagemap', - 'application/x-ipix' => 'ipx', - 'application/x-ipscript' => 'ips', - 'application/x-java-archive' => 'jar', - 'application/x-java-jnlp-file' => 'jnlp', - 'application/x-java-serialized-object' => 'ser', - 'application/x-java-vm' => 'class', - 'application/x-javascript' => 'js', - 'application/x-koan' => 'skp', - 'application/x-latex' => 'latex', - 'application/x-mac-compactpro' => 'cpt', - 'application/x-maker' => 'frm', - 'application/x-mathcad' => 'mcd', - 'application/x-midi' => 'mid', - 'application/x-mif' => 'mif', - 'application/x-msaccess' => 'mda', - 'application/x-msdos-program' => 'com', - 'application/x-msdownload' => 'base64', - 'application/x-msexcel' => 'xls', - 'application/x-msword' => 'doc', - 'application/x-netcdf' => 'nc', - 'application/x-ns-proxy-autoconfig' => 'pac', - 'application/x-pagemaker' => 'pm5', - 'application/x-perl' => 'pl', - 'application/x-pn-realmedia' => 'rp', - 'application/x-python' => 'py', - 'application/x-quicktimeplayer' => 'qtl', - 'application/x-rar-compressed' => 'rar', - 'application/x-ruby' => 'rb', - 'application/x-sh' => 'sh', - 'application/x-shar' => 'shar', - 'application/x-shockwave-flash' => 'swf', - 'application/x-sprite' => 'spr', - 'application/x-spss' => 'sav', - 'application/x-spt' => 'spt', - 'application/x-stuffit' => 'sit', - 'application/x-sv4cpio' => 'sv4cpio', - 'application/x-sv4crc' => 'sv4crc', - 'application/x-tar' => 'tar', - 'application/x-tcl' => 'tcl', - 'application/x-tex' => 'tex', - 'application/x-texinfo' => 'texinfo', - 'application/x-troff' => 't', - 'application/x-troff-man' => 'man', - 'application/x-troff-me' => 'me', - 'application/x-troff-ms' => 'ms', - 'application/x-twinvq' => 'vqf', - 'application/x-twinvq-plugin' => 'vqe', - 'application/x-ustar' => 'ustar', - 'application/x-vmsbackup' => 'bck', - 'application/x-wais-source' => 'src', - 'application/x-wingz' => 'wz', - 'application/x-word' => 'base64', - 'application/x-wordperfect6.1' => 'wp6', - 'application/x-x509-ca-cert' => 'crt', - 'application/x-zip-compressed' => 'zip', - 'application/xhtml+xml' => 'xhtml', - 'application/zip' => 'zip', - 'audio/3gpp' => '3gpp', - 'audio/amr' => 'amr', - 'audio/amr-wb' => 'awb', - 'audio/basic' => 'au', - 'audio/evrc' => 'evc', - 'audio/l16' => 'l16', - 'audio/midi' => 'mid', - 'audio/mpeg' => 'mp3', - 'audio/prs.sid' => 'sid', - 'audio/qcelp' => 'qcp', - 'audio/smv' => 'smv', - 'audio/vnd.audiokoz' => 'koz', - 'audio/vnd.digital-winds' => 'eol', - 'audio/vnd.everad.plj' => 'plj', - 'audio/vnd.lucent.voice' => 'lvp', - 'audio/vnd.nokia.mobile-xmf' => 'mxmf', - 'audio/vnd.nortel.vbk' => 'vbk', - 'audio/vnd.nuera.ecelp4800' => 'ecelp4800', - 'audio/vnd.nuera.ecelp7470' => 'ecelp7470', - 'audio/vnd.nuera.ecelp9600' => 'ecelp9600', - 'audio/vnd.sealedmedia.softseal.mpeg' => 'smp3', - 'audio/voxware' => 'vox', - 'audio/x-aiff' => 'aif', - 'audio/x-mid' => 'mid', - 'audio/x-midi' => 'mid', - 'audio/x-mpeg' => 'mp2', - 'audio/x-mpegurl' => 'mpu', - 'audio/x-pn-realaudio' => 'rm', - 'audio/x-pn-realaudio-plugin' => 'rpm', - 'audio/x-realaudio' => 'ra', - 'audio/x-wav' => 'wav', - 'chemical/x-csml' => 'csm', - 'chemical/x-embl-dl-nucleotide' => 'emb', - 'chemical/x-gaussian-cube' => 'cube', - 'chemical/x-gaussian-input' => 'gau', - 'chemical/x-jcamp-dx' => 'jdx', - 'chemical/x-mdl-molfile' => 'mol', - 'chemical/x-mdl-rxnfile' => 'rxn', - 'chemical/x-mdl-tgf' => 'tgf', - 'chemical/x-mopac-input' => 'mop', - 'chemical/x-pdb' => 'pdb', - 'chemical/x-rasmol' => 'scr', - 'chemical/x-xyz' => 'xyz', - 'drawing/dwf' => 'dwf', - 'drawing/x-dwf' => 'dwf', - 'i-world/i-vrml' => 'ivr', - 'image/bmp' => 'bmp', - 'image/cewavelet' => 'wif', - 'image/cis-cod' => 'cod', - 'image/fif' => 'fif', - 'image/gif' => 'gif', - 'image/ief' => 'ief', - 'image/jp2' => 'jp2', - 'image/jpeg' => 'jpg', - 'image/jpm' => 'jpm', - 'image/jpx' => 'jpf', - 'image/pict' => 'pic', - 'image/pjpeg' => 'jpg', - 'image/png' => 'png', - 'image/targa' => 'tga', - 'image/tiff' => 'tif', - 'image/vn-svf' => 'svf', - 'image/vnd.dgn' => 'dgn', - 'image/vnd.djvu' => 'djvu', - 'image/vnd.dwg' => 'dwg', - 'image/vnd.glocalgraphics.pgb' => 'pgb', - 'image/vnd.microsoft.icon' => 'ico', - 'image/vnd.ms-modi' => 'mdi', - 'image/vnd.sealed.png' => 'spng', - 'image/vnd.sealedmedia.softseal.gif' => 'sgif', - 'image/vnd.sealedmedia.softseal.jpg' => 'sjpg', - 'image/vnd.wap.wbmp' => 'wbmp', - 'image/x-bmp' => 'bmp', - 'image/x-cmu-raster' => 'ras', - 'image/x-freehand' => 'fh4', - 'image/x-png' => 'png', - 'image/x-portable-anymap' => 'pnm', - 'image/x-portable-bitmap' => 'pbm', - 'image/x-portable-graymap' => 'pgm', - 'image/x-portable-pixmap' => 'ppm', - 'image/x-rgb' => 'rgb', - 'image/x-xbitmap' => 'xbm', - 'image/x-xpixmap' => 'xpm', - 'image/x-xwindowdump' => 'xwd', - 'message/external-body' => '8bit', - 'message/news' => '8bit', - 'message/partial' => '8bit', - 'message/rfc822' => '8bit', - 'model/iges' => 'igs', - 'model/mesh' => 'msh', - 'model/vnd.parasolid.transmit.binary' => 'x_b', - 'model/vnd.parasolid.transmit.text' => 'x_t', - 'model/vrml' => 'wrl', - 'multipart/alternative' => '8bit', - 'multipart/appledouble' => '8bit', - 'multipart/digest' => '8bit', - 'multipart/mixed' => '8bit', - 'multipart/parallel' => '8bit', - 'text/comma-separated-values' => 'csv', - 'text/css' => 'css', - 'text/html' => 'html', - 'text/plain' => 'txt', - 'text/prs.fallenstein.rst' => 'rst', - 'text/richtext' => 'rtx', - 'text/rtf' => 'rtf', - 'text/sgml' => 'sgml', - 'text/tab-separated-values' => 'tsv', - 'text/vnd.net2phone.commcenter.command' => 'ccc', - 'text/vnd.sun.j2me.app-descriptor' => 'jad', - 'text/vnd.wap.si' => 'si', - 'text/vnd.wap.sl' => 'sl', - 'text/vnd.wap.wml' => 'wml', - 'text/vnd.wap.wmlscript' => 'wmls', - 'text/x-hdml' => 'hdml', - 'text/x-setext' => 'etx', - 'text/x-sgml' => 'sgml', - 'text/x-speech' => 'talk', - 'text/x-vcalendar' => 'vcs', - 'text/x-vcard' => 'vcf', - 'text/xml' => 'xml', - 'ulead/vrml' => 'uvr', - 'video/3gpp' => '3gp', - 'video/dl' => 'dl', - 'video/gl' => 'gl', - 'video/mj2' => 'mj2', - 'video/mpeg' => 'mpeg', - 'video/quicktime' => 'mov', - 'video/vdo' => 'vdo', - 'video/vivo' => 'viv', - 'video/vnd.fvt' => 'fvt', - 'video/vnd.mpegurl' => 'mxu', - 'video/vnd.nokia.interleaved-multimedia' => 'nim', - 'video/vnd.objectvideo' => 'mp4', - 'video/vnd.sealed.mpeg1' => 's11', - 'video/vnd.sealed.mpeg4' => 'smpg', - 'video/vnd.sealed.swf' => 'sswf', - 'video/vnd.sealedmedia.softseal.mov' => 'smov', - 'video/vnd.vivo' => 'vivo', - 'video/x-fli' => 'fli', - 'video/x-ms-asf' => 'asf', - 'video/x-ms-wmv' => 'wmv', - 'video/x-msvideo' => 'avi', - 'video/x-sgi-movie' => 'movie', - 'x-chemical/x-pdb' => 'pdb', - 'x-chemical/x-xyz' => 'xyz', - 'x-conference/x-cooltalk' => 'ice', - 'x-drawing/dwf' => 'dwf', - 'x-world/x-d96' => 'd', - 'x-world/x-svr' => 'svr', - 'x-world/x-vream' => 'vrw', - 'x-world/x-vrml' => 'wrl', - ); + /** + * Assignment of mime types to their default extensions + * @var array + */ + static protected $defaultExtensions = array( + 'application/andrew-inset' => 'ez', + 'application/appledouble' => 'base64', + 'application/applefile' => 'base64', + 'application/commonground' => 'dp', + 'application/cprplayer' => 'pqi', + 'application/dsptype' => 'tsp', + 'application/excel' => 'xls', + 'application/font-tdpfr' => 'pfr', + 'application/futuresplash' => 'spl', + 'application/hstu' => 'stk', + 'application/hyperstudio' => 'stk', + 'application/javascript' => 'js', + 'application/mac-binhex40' => 'hqx', + 'application/mac-compactpro' => 'cpt', + 'application/mbed' => 'mbd', + 'application/mirage' => 'mfp', + 'application/msword' => 'doc', + 'application/ocsp-request' => 'orq', + 'application/ocsp-response' => 'ors', + 'application/octet-stream' => 'bin', + 'application/oda' => 'oda', + 'application/ogg' => 'ogg', + 'application/pdf' => 'pdf', + 'application/x-pdf' => 'pdf', + 'application/pgp-encrypted' => '7bit', + 'application/pgp-keys' => '7bit', + 'application/pgp-signature' => 'sig', + 'application/pkcs10' => 'p10', + 'application/pkcs7-mime' => 'p7m', + 'application/pkcs7-signature' => 'p7s', + 'application/pkix-cert' => 'cer', + 'application/pkix-crl' => 'crl', + 'application/pkix-pkipath' => 'pkipath', + 'application/pkixcmp' => 'pki', + 'application/postscript' => 'ps', + 'application/presentations' => 'shw', + 'application/prs.cww' => 'cw', + 'application/prs.nprend' => 'rnd', + 'application/quest' => 'qrt', + 'application/rtf' => 'rtf', + 'application/sgml-open-catalog' => 'soc', + 'application/sieve' => 'siv', + 'application/smil' => 'smi', + 'application/toolbook' => 'tbk', + 'application/vnd.3gpp.pic-bw-large' => 'plb', + 'application/vnd.3gpp.pic-bw-small' => 'psb', + 'application/vnd.3gpp.pic-bw-var' => 'pvb', + 'application/vnd.3gpp.sms' => 'sms', + 'application/vnd.acucorp' => 'atc', + 'application/vnd.adobe.xfdf' => 'xfdf', + 'application/vnd.amiga.amu' => 'ami', + 'application/vnd.blueice.multipass' => 'mpm', + 'application/vnd.cinderella' => 'cdy', + 'application/vnd.cosmocaller' => 'cmc', + 'application/vnd.criticaltools.wbs+xml' => 'wbs', + 'application/vnd.curl' => 'curl', + 'application/vnd.data-vision.rdz' => 'rdz', + 'application/vnd.dreamfactory' => 'dfac', + 'application/vnd.fsc.weblauch' => 'fsc', + 'application/vnd.genomatix.tuxedo' => 'txd', + 'application/vnd.hbci' => 'hbci', + 'application/vnd.hhe.lesson-player' => 'les', + 'application/vnd.hp-hpgl' => 'plt', + 'application/vnd.ibm.electronic-media' => 'emm', + 'application/vnd.ibm.rights-management' => 'irm', + 'application/vnd.ibm.secure-container' => 'sc', + 'application/vnd.ipunplugged.rcprofile' => 'rcprofile', + 'application/vnd.irepository.package+xml' => 'irp', + 'application/vnd.jisp' => 'jisp', + 'application/vnd.kde.karbon' => 'karbon', + 'application/vnd.kde.kchart' => 'chrt', + 'application/vnd.kde.kformula' => 'kfo', + 'application/vnd.kde.kivio' => 'flw', + 'application/vnd.kde.kontour' => 'kon', + 'application/vnd.kde.kpresenter' => 'kpr', + 'application/vnd.kde.kspread' => 'ksp', + 'application/vnd.kde.kword' => 'kwd', + 'application/vnd.kenameapp' => 'htke', + 'application/vnd.kidspiration' => 'kia', + 'application/vnd.kinar' => 'kne', + 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd', + 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe', + 'application/vnd.lotus-1-2-3' => 'wks', + 'application/vnd.mcd' => 'mcd', + 'application/vnd.mfmp' => 'mfm', + 'application/vnd.micrografx.flo' => 'flo', + 'application/vnd.micrografx.igx' => 'igx', + 'application/vnd.mif' => 'mif', + 'application/vnd.mophun.application' => 'mpn', + 'application/vnd.mophun.certificate' => 'mpc', + 'application/vnd.mozilla.xul+xml' => 'xul', + 'application/vnd.ms-artgalry' => 'cil', + 'application/vnd.ms-asf' => 'asf', + 'application/vnd.ms-excel' => 'xls', + 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm', + 'application/vnd.ms-lrm' => 'lrm', + 'application/vnd.ms-powerpoint' => 'ppt', + 'application/vnd.ms-project' => 'mpp', + 'application/vnd.ms-tnef' => 'base64', + 'application/vnd.ms-works' => 'base64', + 'application/vnd.ms-wpl' => 'wpl', + 'application/vnd.mseq' => 'mseq', + 'application/vnd.nervana' => 'ent', + 'application/vnd.nokia.radio-preset' => 'rpst', + 'application/vnd.nokia.radio-presets' => 'rpss', + 'application/vnd.oasis.opendocument.text' => 'odt', + 'application/vnd.oasis.opendocument.text-template' => 'ott', + 'application/vnd.oasis.opendocument.text-web' => 'oth', + 'application/vnd.oasis.opendocument.text-master' => 'odm', + 'application/vnd.oasis.opendocument.graphics' => 'odg', + 'application/vnd.oasis.opendocument.graphics-template' => 'otg', + 'application/vnd.oasis.opendocument.presentation' => 'odp', + 'application/vnd.oasis.opendocument.presentation-template' => 'otp', + 'application/vnd.oasis.opendocument.spreadsheet' => 'ods', + 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots', + 'application/vnd.oasis.opendocument.chart' => 'odc', + 'application/vnd.oasis.opendocument.formula' => 'odf', + 'application/vnd.oasis.opendocument.database' => 'odb', + 'application/vnd.oasis.opendocument.image' => 'odi', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx', + 'application/vnd.palm' => 'prc', + 'application/vnd.picsel' => 'efif', + 'application/vnd.pvi.ptid1' => 'pti', + 'application/vnd.quark.quarkxpress' => 'qxd', + 'application/vnd.sealed.doc' => 'sdoc', + 'application/vnd.sealed.eml' => 'seml', + 'application/vnd.sealed.mht' => 'smht', + 'application/vnd.sealed.ppt' => 'sppt', + 'application/vnd.sealed.xls' => 'sxls', + 'application/vnd.sealedmedia.softseal.html' => 'stml', + 'application/vnd.sealedmedia.softseal.pdf' => 'spdf', + 'application/vnd.seemail' => 'see', + 'application/vnd.smaf' => 'mmf', + 'application/vnd.sun.xml.calc' => 'sxc', + 'application/vnd.sun.xml.calc.template' => 'stc', + 'application/vnd.sun.xml.draw' => 'sxd', + 'application/vnd.sun.xml.draw.template' => 'std', + 'application/vnd.sun.xml.impress' => 'sxi', + 'application/vnd.sun.xml.impress.template' => 'sti', + 'application/vnd.sun.xml.math' => 'sxm', + 'application/vnd.sun.xml.writer' => 'sxw', + 'application/vnd.sun.xml.writer.global' => 'sxg', + 'application/vnd.sun.xml.writer.template' => 'stw', + 'application/vnd.sus-calendar' => 'sus', + 'application/vnd.vidsoft.vidconference' => 'vsc', + 'application/vnd.visio' => 'vsd', + 'application/vnd.visionary' => 'vis', + 'application/vnd.wap.sic' => 'sic', + 'application/vnd.wap.slc' => 'slc', + 'application/vnd.wap.wbxml' => 'wbxml', + 'application/vnd.wap.wmlc' => 'wmlc', + 'application/vnd.wap.wmlscriptc' => 'wmlsc', + 'application/vnd.webturbo' => 'wtb', + 'application/vnd.wordperfect' => 'wpd', + 'application/vnd.wqd' => 'wqd', + 'application/vnd.wv.csp+wbxml' => 'wv', + 'application/vnd.wv.csp+xml' => '8bit', + 'application/vnd.wv.ssp+xml' => '8bit', + 'application/vnd.yamaha.hv-dic' => 'hvd', + 'application/vnd.yamaha.hv-script' => 'hvs', + 'application/vnd.yamaha.hv-voice' => 'hvp', + 'application/vnd.yamaha.smaf-audio' => 'saf', + 'application/vnd.yamaha.smaf-phrase' => 'spf', + 'application/vocaltec-media-desc' => 'vmd', + 'application/vocaltec-media-file' => 'vmf', + 'application/vocaltec-talker' => 'vtk', + 'application/watcherinfo+xml' => 'wif', + 'application/wordperfect5.1' => 'wp5', + 'application/x-123' => 'wk', + 'application/x-7th_level_event' => '7ls', + 'application/x-authorware-bin' => 'aab', + 'application/x-authorware-map' => 'aam', + 'application/x-authorware-seg' => 'aas', + 'application/x-bcpio' => 'bcpio', + 'application/x-bleeper' => 'bleep', + 'application/x-bzip2' => 'bz2', + 'application/x-cdlink' => 'vcd', + 'application/x-chat' => 'chat', + 'application/x-chess-pgn' => 'pgn', + 'application/x-compress' => 'z', + 'application/x-cpio' => 'cpio', + 'application/x-cprplayer' => 'pqf', + 'application/x-csh' => 'csh', + 'application/x-cu-seeme' => 'csm', + 'application/x-cult3d-object' => 'co', + 'application/x-debian-package' => 'deb', + 'application/x-director' => 'dcr', + 'application/x-dvi' => 'dvi', + 'application/x-envoy' => 'evy', + 'application/x-futuresplash' => 'spl', + 'application/x-gtar' => 'gtar', + 'application/x-gzip' => 'gz', + 'application/x-hdf' => 'hdf', + 'application/x-hep' => 'hep', + 'application/x-html+ruby' => 'rhtml', + 'application/x-httpd-miva' => 'mv', + 'application/x-httpd-php' => 'phtml', + 'application/x-ica' => 'ica', + 'application/x-imagemap' => 'imagemap', + 'application/x-ipix' => 'ipx', + 'application/x-ipscript' => 'ips', + 'application/x-java-archive' => 'jar', + 'application/x-java-jnlp-file' => 'jnlp', + 'application/x-java-serialized-object' => 'ser', + 'application/x-java-vm' => 'class', + 'application/x-javascript' => 'js', + 'application/x-koan' => 'skp', + 'application/x-latex' => 'latex', + 'application/x-mac-compactpro' => 'cpt', + 'application/x-maker' => 'frm', + 'application/x-mathcad' => 'mcd', + 'application/x-midi' => 'mid', + 'application/x-mif' => 'mif', + 'application/x-msaccess' => 'mda', + 'application/x-msdos-program' => 'com', + 'application/x-msdownload' => 'base64', + 'application/x-msexcel' => 'xls', + 'application/x-msword' => 'doc', + 'application/x-netcdf' => 'nc', + 'application/x-ns-proxy-autoconfig' => 'pac', + 'application/x-pagemaker' => 'pm5', + 'application/x-perl' => 'pl', + 'application/x-pn-realmedia' => 'rp', + 'application/x-python' => 'py', + 'application/x-quicktimeplayer' => 'qtl', + 'application/x-rar-compressed' => 'rar', + 'application/x-ruby' => 'rb', + 'application/x-sh' => 'sh', + 'application/x-shar' => 'shar', + 'application/x-shockwave-flash' => 'swf', + 'application/x-sprite' => 'spr', + 'application/x-spss' => 'sav', + 'application/x-spt' => 'spt', + 'application/x-stuffit' => 'sit', + 'application/x-sv4cpio' => 'sv4cpio', + 'application/x-sv4crc' => 'sv4crc', + 'application/x-tar' => 'tar', + 'application/x-tcl' => 'tcl', + 'application/x-tex' => 'tex', + 'application/x-texinfo' => 'texinfo', + 'application/x-troff' => 't', + 'application/x-troff-man' => 'man', + 'application/x-troff-me' => 'me', + 'application/x-troff-ms' => 'ms', + 'application/x-twinvq' => 'vqf', + 'application/x-twinvq-plugin' => 'vqe', + 'application/x-ustar' => 'ustar', + 'application/x-vmsbackup' => 'bck', + 'application/x-wais-source' => 'src', + 'application/x-wingz' => 'wz', + 'application/x-word' => 'base64', + 'application/x-wordperfect6.1' => 'wp6', + 'application/x-x509-ca-cert' => 'crt', + 'application/x-zip-compressed' => 'zip', + 'application/xhtml+xml' => 'xhtml', + 'application/zip' => 'zip', + 'audio/3gpp' => '3gpp', + 'audio/amr' => 'amr', + 'audio/amr-wb' => 'awb', + 'audio/basic' => 'au', + 'audio/evrc' => 'evc', + 'audio/l16' => 'l16', + 'audio/midi' => 'mid', + 'audio/mpeg' => 'mp3', + 'audio/prs.sid' => 'sid', + 'audio/qcelp' => 'qcp', + 'audio/smv' => 'smv', + 'audio/vnd.audiokoz' => 'koz', + 'audio/vnd.digital-winds' => 'eol', + 'audio/vnd.everad.plj' => 'plj', + 'audio/vnd.lucent.voice' => 'lvp', + 'audio/vnd.nokia.mobile-xmf' => 'mxmf', + 'audio/vnd.nortel.vbk' => 'vbk', + 'audio/vnd.nuera.ecelp4800' => 'ecelp4800', + 'audio/vnd.nuera.ecelp7470' => 'ecelp7470', + 'audio/vnd.nuera.ecelp9600' => 'ecelp9600', + 'audio/vnd.sealedmedia.softseal.mpeg' => 'smp3', + 'audio/voxware' => 'vox', + 'audio/x-aiff' => 'aif', + 'audio/x-mid' => 'mid', + 'audio/x-midi' => 'mid', + 'audio/x-mpeg' => 'mp2', + 'audio/x-mpegurl' => 'mpu', + 'audio/x-pn-realaudio' => 'rm', + 'audio/x-pn-realaudio-plugin' => 'rpm', + 'audio/x-realaudio' => 'ra', + 'audio/x-wav' => 'wav', + 'chemical/x-csml' => 'csm', + 'chemical/x-embl-dl-nucleotide' => 'emb', + 'chemical/x-gaussian-cube' => 'cube', + 'chemical/x-gaussian-input' => 'gau', + 'chemical/x-jcamp-dx' => 'jdx', + 'chemical/x-mdl-molfile' => 'mol', + 'chemical/x-mdl-rxnfile' => 'rxn', + 'chemical/x-mdl-tgf' => 'tgf', + 'chemical/x-mopac-input' => 'mop', + 'chemical/x-pdb' => 'pdb', + 'chemical/x-rasmol' => 'scr', + 'chemical/x-xyz' => 'xyz', + 'drawing/dwf' => 'dwf', + 'drawing/x-dwf' => 'dwf', + 'i-world/i-vrml' => 'ivr', + 'image/bmp' => 'bmp', + 'image/cewavelet' => 'wif', + 'image/cis-cod' => 'cod', + 'image/fif' => 'fif', + 'image/gif' => 'gif', + 'image/ief' => 'ief', + 'image/jp2' => 'jp2', + 'image/jpeg' => 'jpg', + 'image/jpm' => 'jpm', + 'image/jpx' => 'jpf', + 'image/pict' => 'pic', + 'image/pjpeg' => 'jpg', + 'image/png' => 'png', + 'image/targa' => 'tga', + 'image/tiff' => 'tif', + 'image/vn-svf' => 'svf', + 'image/vnd.dgn' => 'dgn', + 'image/vnd.djvu' => 'djvu', + 'image/vnd.dwg' => 'dwg', + 'image/vnd.glocalgraphics.pgb' => 'pgb', + 'image/vnd.microsoft.icon' => 'ico', + 'image/vnd.ms-modi' => 'mdi', + 'image/vnd.sealed.png' => 'spng', + 'image/vnd.sealedmedia.softseal.gif' => 'sgif', + 'image/vnd.sealedmedia.softseal.jpg' => 'sjpg', + 'image/vnd.wap.wbmp' => 'wbmp', + 'image/x-bmp' => 'bmp', + 'image/x-cmu-raster' => 'ras', + 'image/x-freehand' => 'fh4', + 'image/x-png' => 'png', + 'image/x-portable-anymap' => 'pnm', + 'image/x-portable-bitmap' => 'pbm', + 'image/x-portable-graymap' => 'pgm', + 'image/x-portable-pixmap' => 'ppm', + 'image/x-rgb' => 'rgb', + 'image/x-xbitmap' => 'xbm', + 'image/x-xpixmap' => 'xpm', + 'image/x-xwindowdump' => 'xwd', + 'message/external-body' => '8bit', + 'message/news' => '8bit', + 'message/partial' => '8bit', + 'message/rfc822' => '8bit', + 'model/iges' => 'igs', + 'model/mesh' => 'msh', + 'model/vnd.parasolid.transmit.binary' => 'x_b', + 'model/vnd.parasolid.transmit.text' => 'x_t', + 'model/vrml' => 'wrl', + 'multipart/alternative' => '8bit', + 'multipart/appledouble' => '8bit', + 'multipart/digest' => '8bit', + 'multipart/mixed' => '8bit', + 'multipart/parallel' => '8bit', + 'text/comma-separated-values' => 'csv', + 'text/css' => 'css', + 'text/html' => 'html', + 'text/plain' => 'txt', + 'text/prs.fallenstein.rst' => 'rst', + 'text/richtext' => 'rtx', + 'text/rtf' => 'rtf', + 'text/sgml' => 'sgml', + 'text/tab-separated-values' => 'tsv', + 'text/vnd.net2phone.commcenter.command' => 'ccc', + 'text/vnd.sun.j2me.app-descriptor' => 'jad', + 'text/vnd.wap.si' => 'si', + 'text/vnd.wap.sl' => 'sl', + 'text/vnd.wap.wml' => 'wml', + 'text/vnd.wap.wmlscript' => 'wmls', + 'text/x-hdml' => 'hdml', + 'text/x-setext' => 'etx', + 'text/x-sgml' => 'sgml', + 'text/x-speech' => 'talk', + 'text/x-vcalendar' => 'vcs', + 'text/x-vcard' => 'vcf', + 'text/xml' => 'xml', + 'ulead/vrml' => 'uvr', + 'video/3gpp' => '3gp', + 'video/dl' => 'dl', + 'video/gl' => 'gl', + 'video/mj2' => 'mj2', + 'video/mpeg' => 'mpeg', + 'video/quicktime' => 'mov', + 'video/vdo' => 'vdo', + 'video/vivo' => 'viv', + 'video/vnd.fvt' => 'fvt', + 'video/vnd.mpegurl' => 'mxu', + 'video/vnd.nokia.interleaved-multimedia' => 'nim', + 'video/vnd.objectvideo' => 'mp4', + 'video/vnd.sealed.mpeg1' => 's11', + 'video/vnd.sealed.mpeg4' => 'smpg', + 'video/vnd.sealed.swf' => 'sswf', + 'video/vnd.sealedmedia.softseal.mov' => 'smov', + 'video/vnd.vivo' => 'vivo', + 'video/x-fli' => 'fli', + 'video/x-ms-asf' => 'asf', + 'video/x-ms-wmv' => 'wmv', + 'video/x-msvideo' => 'avi', + 'video/x-sgi-movie' => 'movie', + 'x-chemical/x-pdb' => 'pdb', + 'x-chemical/x-xyz' => 'xyz', + 'x-conference/x-cooltalk' => 'ice', + 'x-drawing/dwf' => 'dwf', + 'x-world/x-d96' => 'd', + 'x-world/x-svr' => 'svr', + 'x-world/x-vream' => 'vrw', + 'x-world/x-vrml' => 'wrl', + ); - /** - * The absolute path to the file without dots - * @var string - */ - protected $path; + /** + * The absolute path to the file without dots + * @var string + */ + protected $path; - /** - * Constructs a new file from the given path. - * - * @param string $path The path to the file - * @throws FileNotFoundException If the given path is no file - */ - public function __construct($path) - { - if (!is_file($path)) + /** + * Constructs a new file from the given path. + * + * @param string $path The path to the file + * @throws FileNotFoundException If the given path is no file + */ + public function __construct($path) { - throw new FileNotFoundException($path); + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + $this->path = realpath($path); } - $this->path = realpath($path); - } - - /** - * Alias for getPath() - * - * @return string - */ - public function __toString() - { - return $this->getPath(); - } - - /** - * Returns the file name - * - * @return string - */ - public function getName() - { - return basename($this->path); - } - - /** - * Returns the file extension (with dot) - * - * @return string - */ - public function getExtension() - { - $name = $this->getName(); - - if (false !== ($pos = strrpos($name, '.'))) + /** + * Alias for getPath() + * + * @return string + */ + public function __toString() { - return substr($name, $pos); - } - else - { - return ''; - } - } - - /** - * Returns the extension based on the mime type (with dot) - * - * If the mime type is unknown, the actual extension is returned instead. - * - * @return string - */ - public function getDefaultExtension() - { - $type = $this->getMimeType(); - - if (isset(self::$defaultExtensions[$type])) - { - return '.' . self::$defaultExtensions[$type]; - } - else - { - return $this->getExtension(); - } - } - - /** - * Returns the directory of the file - * - * @return string - */ - public function getDirectory() - { - return dirname($this->path); - } - - /** - * Returns the absolute file path without dots - * - * @returns string The file path - */ - public function getPath() - { - return $this->path; - } - - /** - * Returns the mime type of the file. - * - * The mime type is guessed using the functions finfo(), mime_content_type() - * and the system binary "file" (in this order), depending on which of those - * is available on the current operating system. - * - * @returns string The guessed mime type, e.g. "application/pdf" - */ - public function getMimeType() - { - $guesser = MimeTypeGuesser::getInstance(); - - return $guesser->guess($this->getPath()); - } - - /** - * Returns the size of this file - * - * @return integer The file size in bytes - */ - public function size() - { - if (false === ($size = filesize($this->getPath()))) - { - throw new FileException(sprintf('Could not read file size of %s', $this->getPath())); + return $this->getPath(); } - return $size; - } - - /** - * Moves the file to a new location. - * - * @param string $newPath - */ - public function move($newPath) - { - if (!rename($this->getPath(), $newPath)) + /** + * Returns the file name + * + * @return string + */ + public function getName() { - throw new FileException(sprintf('Could not move file %s to %s', $this->getPath(), $newPath)); + return basename($this->path); + } + + /** + * Returns the file extension (with dot) + * + * @return string + */ + public function getExtension() + { + $name = $this->getName(); + + if (false !== ($pos = strrpos($name, '.'))) { + return substr($name, $pos); + } else { + return ''; + } + } + + /** + * Returns the extension based on the mime type (with dot) + * + * If the mime type is unknown, the actual extension is returned instead. + * + * @return string + */ + public function getDefaultExtension() + { + $type = $this->getMimeType(); + + if (isset(self::$defaultExtensions[$type])) { + return '.' . self::$defaultExtensions[$type]; + } else { + return $this->getExtension(); + } + } + + /** + * Returns the directory of the file + * + * @return string + */ + public function getDirectory() + { + return dirname($this->path); + } + + /** + * Returns the absolute file path without dots + * + * @returns string The file path + */ + public function getPath() + { + return $this->path; + } + + /** + * Returns the mime type of the file. + * + * The mime type is guessed using the functions finfo(), mime_content_type() + * and the system binary "file" (in this order), depending on which of those + * is available on the current operating system. + * + * @returns string The guessed mime type, e.g. "application/pdf" + */ + public function getMimeType() + { + $guesser = MimeTypeGuesser::getInstance(); + + return $guesser->guess($this->getPath()); + } + + /** + * Returns the size of this file + * + * @return integer The file size in bytes + */ + public function size() + { + if (false === ($size = filesize($this->getPath()))) { + throw new FileException(sprintf('Could not read file size of %s', $this->getPath())); + } + + return $size; + } + + /** + * Moves the file to a new location. + * + * @param string $newPath + */ + public function move($newPath) + { + if (!rename($this->getPath(), $newPath)) { + throw new FileException(sprintf('Could not move file %s to %s', $this->getPath(), $newPath)); + } } - } } \ No newline at end of file diff --git a/src/Symfony/Components/File/MimeType/ContentTypeMimeTypeGuesser.php b/src/Symfony/Components/File/MimeType/ContentTypeMimeTypeGuesser.php index 4d7644095f..4696d5276a 100644 --- a/src/Symfony/Components/File/MimeType/ContentTypeMimeTypeGuesser.php +++ b/src/Symfony/Components/File/MimeType/ContentTypeMimeTypeGuesser.php @@ -20,46 +20,42 @@ use Symfony\Components\File\Exception\AccessDeniedException; */ class ContentTypeMimeTypeGuesser implements MimeTypeGuesserInterface { - /** - * Returns whether this guesser is supported on the corrent OS/PHP setup - * - * @return boolean - */ - static public function isSupported() - { - return function_exists('mime_content_type'); - } - - /** - * Guesses the mime type of the file with the given path - * - * @see MimeTypeGuesserInterface::guess() - */ - public function guess($path) - { - if (!is_file($path)) + /** + * Returns whether this guesser is supported on the corrent OS/PHP setup + * + * @return boolean + */ + static public function isSupported() { - throw new FileNotFoundException($path); + return function_exists('mime_content_type'); } - if (!is_readable($path)) + /** + * Guesses the mime type of the file with the given path + * + * @see MimeTypeGuesserInterface::guess() + */ + public function guess($path) { - throw new AccessDeniedException($path); + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported() || !is_readable($path)) { + return null; + } + + $type = mime_content_type($path); + + // remove charset (added as of PHP 5.3) + if (false !== $pos = strpos($type, ';')) { + $type = substr($type, 0, $pos); + } + + return $type; } - - if (!self::isSupported() || !is_readable($path)) - { - return null; - } - - $type = mime_content_type($path); - - // remove charset (added as of PHP 5.3) - if (false !== $pos = strpos($type, ';')) - { - $type = substr($type, 0, $pos); - } - - return $type; - } } diff --git a/src/Symfony/Components/File/MimeType/FileBinaryMimeTypeGuesser.php b/src/Symfony/Components/File/MimeType/FileBinaryMimeTypeGuesser.php index 98fc099983..3a213b3970 100644 --- a/src/Symfony/Components/File/MimeType/FileBinaryMimeTypeGuesser.php +++ b/src/Symfony/Components/File/MimeType/FileBinaryMimeTypeGuesser.php @@ -20,42 +20,38 @@ use Symfony\Components\File\Exception\AccessDeniedException; */ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface { - /** - * Guesses the mime type of the file with the given path - * - * @see MimeTypeGuesserInterface::guess() - */ - public function guess($path) - { - if (!is_file($path)) + /** + * Guesses the mime type of the file with the given path + * + * @see MimeTypeGuesserInterface::guess() + */ + public function guess($path) { - throw new FileNotFoundException($path); + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + ob_start(); + + // need to use --mime instead of -i. see #6641 + passthru(sprintf('file -b --mime %s 2>/dev/null', escapeshellarg($path)), $return); + if ($return > 0) { + ob_end_clean(); + + return null; + } + + $type = trim(ob_get_clean()); + + if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-]+)#i', $type, $match)) { + // it's not a type, but an error message + return null; + } + + return $match[1]; } - - if (!is_readable($path)) - { - throw new AccessDeniedException($path); - } - - ob_start(); - - // need to use --mime instead of -i. see #6641 - passthru(sprintf('file -b --mime %s 2>/dev/null', escapeshellarg($path)), $return); - if ($return > 0) - { - ob_end_clean(); - - return null; - } - - $type = trim(ob_get_clean()); - - if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-]+)#i', $type, $match)) - { - // it's not a type, but an error message - return null; - } - - return $match[1]; - } } \ No newline at end of file diff --git a/src/Symfony/Components/File/MimeType/FileinfoMimeTypeGuesser.php b/src/Symfony/Components/File/MimeType/FileinfoMimeTypeGuesser.php index 455a38c6ea..324ad4b476 100644 --- a/src/Symfony/Components/File/MimeType/FileinfoMimeTypeGuesser.php +++ b/src/Symfony/Components/File/MimeType/FileinfoMimeTypeGuesser.php @@ -20,51 +20,46 @@ use Symfony\Components\File\Exception\AccessDeniedException; */ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface { - /** - * Returns whether this guesser is supported on the corrent OS/PHP setup - * - * @return boolean - */ - static public function isSupported() - { - return function_exists('finfo_open'); - } - - /** - * Guesses the mime type of the file with the given path - * - * @see MimeTypeGuesserInterface::guess() - */ - public function guess($path) - { - if (!is_file($path)) + /** + * Returns whether this guesser is supported on the corrent OS/PHP setup + * + * @return boolean + */ + static public function isSupported() { - throw new FileNotFoundException($path); + return function_exists('finfo_open'); } - if (!is_readable($path)) + /** + * Guesses the mime type of the file with the given path + * + * @see MimeTypeGuesserInterface::guess() + */ + public function guess($path) { - throw new AccessDeniedException($path); + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported()) { + return null; + } + + if (!$finfo = new \finfo(FILEINFO_MIME)) { + return null; + } + + $type = $finfo->file($path); + + // remove charset (added as of PHP 5.3) + if (false !== $pos = strpos($type, ';')) { + $type = substr($type, 0, $pos); + } + + return $type; } - - if (!self::isSupported()) - { - return null; - } - - if (!$finfo = new \finfo(FILEINFO_MIME)) - { - return null; - } - - $type = $finfo->file($path); - - // remove charset (added as of PHP 5.3) - if (false !== $pos = strpos($type, ';')) - { - $type = substr($type, 0, $pos); - } - - return $type; - } } \ No newline at end of file diff --git a/src/Symfony/Components/File/MimeType/MimeTypeGuesser.php b/src/Symfony/Components/File/MimeType/MimeTypeGuesser.php index 31c9634875..a3efcd0ce1 100644 --- a/src/Symfony/Components/File/MimeType/MimeTypeGuesser.php +++ b/src/Symfony/Components/File/MimeType/MimeTypeGuesser.php @@ -31,99 +31,92 @@ use Symfony\Components\File\Exception\AccessDeniedException; */ class MimeTypeGuesser implements MimeTypeGuesserInterface { - /** - * The singleton instance - * @var MimeTypeGuesser - */ - static private $instance = null; + /** + * The singleton instance + * @var MimeTypeGuesser + */ + static private $instance = null; - /** - * All registered MimeTypeGuesserInterface instances - * @var array - */ - protected $guessers = array(); + /** + * All registered MimeTypeGuesserInterface instances + * @var array + */ + protected $guessers = array(); - /** - * Returns the singleton instance - * - * @return MimeTypeGuesser - */ - static public function getInstance() - { - if (is_null(self::$instance)) + /** + * Returns the singleton instance + * + * @return MimeTypeGuesser + */ + static public function getInstance() { - self::$instance = new self(); + if (is_null(self::$instance)) { + self::$instance = new self(); + } + + return self::$instance; } - return self::$instance; - } - - /** - * Registers all natively provided mime type guessers - */ - private function __construct() - { - $this->register(new FileBinaryMimeTypeGuesser()); - - if (ContentTypeMimeTypeGuesser::isSupported()) + /** + * Registers all natively provided mime type guessers + */ + private function __construct() { - $this->register(new ContentTypeMimeTypeGuesser()); + $this->register(new FileBinaryMimeTypeGuesser()); + + if (ContentTypeMimeTypeGuesser::isSupported()) { + $this->register(new ContentTypeMimeTypeGuesser()); + } + + if (FileinfoMimeTypeGuesser::isSupported()) { + $this->register(new FileinfoMimeTypeGuesser()); + } } - if (FileinfoMimeTypeGuesser::isSupported()) + /** + * Registers a new mime type guesser + * + * When guessing, this guesser is preferred over previously registered ones. + * + * @param MimeTypeGuesserInterface $guesser + */ + public function register(MimeTypeGuesserInterface $guesser) { - $this->register(new FileinfoMimeTypeGuesser()); - } - } - - /** - * Registers a new mime type guesser - * - * When guessing, this guesser is preferred over previously registered ones. - * - * @param MimeTypeGuesserInterface $guesser - */ - public function register(MimeTypeGuesserInterface $guesser) - { - array_unshift($this->guessers, $guesser); - } - - /** - * Tries to guess the mime type of the given file - * - * The file is passed to each registered mime type guesser in reverse order - * of their registration (last registered is queried first). Once a guesser - * returns a value that is not NULL, this method terminates and returns the - * value. - * - * @param string $path The path to the file - * @return string The mime type or NULL, if none could be guessed - * @throws FileException If the file does not exist - */ - public function guess($path) - { - if (!is_file($path)) - { - throw new FileNotFoundException($path); + array_unshift($this->guessers, $guesser); } - if (!is_readable($path)) + /** + * Tries to guess the mime type of the given file + * + * The file is passed to each registered mime type guesser in reverse order + * of their registration (last registered is queried first). Once a guesser + * returns a value that is not NULL, this method terminates and returns the + * value. + * + * @param string $path The path to the file + * @return string The mime type or NULL, if none could be guessed + * @throws FileException If the file does not exist + */ + public function guess($path) { - throw new AccessDeniedException($path); + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + $mimeType = null; + + foreach ($this->guessers as $guesser) { + $mimeType = $guesser->guess($path); + + if (!is_null($mimeType)) { + break; + } + } + + return $mimeType; } - - $mimeType = null; - - foreach ($this->guessers as $guesser) - { - $mimeType = $guesser->guess($path); - - if (!is_null($mimeType)) - { - break; - } - } - - return $mimeType; - } } \ No newline at end of file diff --git a/src/Symfony/Components/File/MimeType/MimeTypeGuesserInterface.php b/src/Symfony/Components/File/MimeType/MimeTypeGuesserInterface.php index e29ce2fd34..1f1a294814 100644 --- a/src/Symfony/Components/File/MimeType/MimeTypeGuesserInterface.php +++ b/src/Symfony/Components/File/MimeType/MimeTypeGuesserInterface.php @@ -17,13 +17,13 @@ namespace Symfony\Components\File\MimeType; */ interface MimeTypeGuesserInterface { - /** - * Guesses the mime type of the file with the given path - * - * @param string $path The path to the file - * @return string The mime type or NULL, if none could be guessed - * @throws FileNotFoundException If the file does not exist - * @throws AccessDeniedException If the file could not be read - */ - public function guess($path); + /** + * Guesses the mime type of the file with the given path + * + * @param string $path The path to the file + * @return string The mime type or NULL, if none could be guessed + * @throws FileNotFoundException If the file does not exist + * @throws AccessDeniedException If the file could not be read + */ + public function guess($path); } \ No newline at end of file diff --git a/src/Symfony/Components/File/UploadedFile.php b/src/Symfony/Components/File/UploadedFile.php index 73524a5fa0..b7aa2c9d99 100644 --- a/src/Symfony/Components/File/UploadedFile.php +++ b/src/Symfony/Components/File/UploadedFile.php @@ -20,111 +20,103 @@ use Symfony\Components\File\Exception\FileException; */ class UploadedFile extends File { - protected $originalName; - protected $mimeType; - protected $size; - protected $error; - protected $moved = false; + protected $originalName; + protected $mimeType; + protected $size; + protected $error; + protected $moved = false; - /** - * Accepts the information of the uploaded file as provided by the PHP - * global $_FILES. - * - * @param string $tmpName The full temporary path to the file - * @param string $name The original file name - * @param string $type The type of the file as provided by PHP - * @param integer $size The file size - * @param string $error The error constant of the upload. Should be - * one of PHP's UPLOAD_XXX constants. - */ - public function __construct($path, $originalName, $mimeType, $size, $error) - { - if (!ini_get('file_uploads')) + /** + * Accepts the information of the uploaded file as provided by the PHP + * global $_FILES. + * + * @param string $tmpName The full temporary path to the file + * @param string $name The original file name + * @param string $type The type of the file as provided by PHP + * @param integer $size The file size + * @param string $error The error constant of the upload. Should be + * one of PHP's UPLOAD_XXX constants. + */ + public function __construct($path, $originalName, $mimeType, $size, $error) { - throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path'))); + if (!ini_get('file_uploads')) { + throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path'))); + } + + parent::__construct($path); + + if (is_null($error)) { + $error = UPLOAD_ERR_OK; + } + + if (is_null($mimeType)) { + $mimeType = 'application/octet-stream'; + } + + $this->originalName = (string)$originalName; + $this->mimeType = $mimeType; + $this->size = $size; + $this->error = $error; } - parent::__construct($path); - - if (is_null($error)) + /** + * Returns the mime type of the file. + * + * The mime type is guessed using the functions finfo(), mime_content_type() + * and the system binary "file" (in this order), depending on which of those + * is available on the current operating system. + * + * @returns string The guessed mime type, e.g. "application/pdf" + */ + public function getMimeType() { - $error = UPLOAD_ERR_OK; + $mimeType = parent::getMimeType(); + + if (is_null($mimeType)) { + $mimeType = $this->mimeType; + } + + return $mimeType; } - if (is_null($mimeType)) + /** + * Returns the original file name including its extension. + * + * @returns string The file name + */ + public function getOriginalName() { - $mimeType = 'application/octet-stream'; + return $this->originalName; } - $this->originalName = (string)$originalName; - $this->mimeType = $mimeType; - $this->size = $size; - $this->error = $error; - } - - /** - * Returns the mime type of the file. - * - * The mime type is guessed using the functions finfo(), mime_content_type() - * and the system binary "file" (in this order), depending on which of those - * is available on the current operating system. - * - * @returns string The guessed mime type, e.g. "application/pdf" - */ - public function getMimeType() - { - $mimeType = parent::getMimeType(); - - if (is_null($mimeType)) + /** + * Returns the upload error. + * + * If the upload was successful, the constant UPLOAD_ERR_OK is returned. + * Otherwise one of the other UPLOAD_ERR_XXX constants is returned. + * + * @returns integer The upload error + */ + public function getError() { - $mimeType = $this->mimeType; + return $this->error; } - return $mimeType; - } - - /** - * Returns the original file name including its extension. - * - * @returns string The file name - */ - public function getOriginalName() - { - return $this->originalName; - } - - /** - * Returns the upload error. - * - * If the upload was successful, the constant UPLOAD_ERR_OK is returned. - * Otherwise one of the other UPLOAD_ERR_XXX constants is returned. - * - * @returns integer The upload error - */ - public function getError() - { - return $this->error; - } - - /** - * Moves the file to a new location. - * - * @param string $newPath - */ - public function move($newPath) - { - if (!$this->moved) + /** + * Moves the file to a new location. + * + * @param string $newPath + */ + public function move($newPath) { - if (!move_uploaded_file($this->getPath(), $newPath)) - { - throw new FileException(sprintf('Could not move file %s to %s', $this->getPath(), $newPath)); - } + if (!$this->moved) { + if (!move_uploaded_file($this->getPath(), $newPath)) { + throw new FileException(sprintf('Could not move file %s to %s', $this->getPath(), $newPath)); + } - $this->moved = true; + $this->moved = true; + } else { + parent::move($newPath); + } } - else - { - parent::move($newPath); - } - } } \ No newline at end of file diff --git a/src/Symfony/Components/Form/BirthdayField.php b/src/Symfony/Components/Form/BirthdayField.php index 34745cc67b..f7bbb1713c 100644 --- a/src/Symfony/Components/Form/BirthdayField.php +++ b/src/Symfony/Components/Form/BirthdayField.php @@ -20,15 +20,15 @@ namespace Symfony\Components\Form; */ class BirthdayField extends DateField { - /** - * {@inheritDoc} - */ - protected function configure() - { - $currentYear = date('Y'); + /** + * {@inheritDoc} + */ + protected function configure() + { + $currentYear = date('Y'); - $this->addOption('years', range($currentYear-120, $currentYear)); + $this->addOption('years', range($currentYear-120, $currentYear)); - parent::configure(); - } + parent::configure(); + } } \ No newline at end of file diff --git a/src/Symfony/Components/Form/CheckboxField.php b/src/Symfony/Components/Form/CheckboxField.php index 1fae41e0d7..728de9d910 100644 --- a/src/Symfony/Components/Form/CheckboxField.php +++ b/src/Symfony/Components/Form/CheckboxField.php @@ -20,13 +20,13 @@ use Symfony\Components\Form\ValueTransformer\BooleanToStringTransformer; */ class CheckboxField extends ToggleField { - /** - * {@inheritDoc} - */ - public function render(array $attributes = array()) - { - return parent::render(array_merge(array( - 'type' => 'checkbox', - ), $attributes)); - } + /** + * {@inheritDoc} + */ + public function render(array $attributes = array()) + { + return parent::render(array_merge(array( + 'type' => 'checkbox', + ), $attributes)); + } } \ No newline at end of file diff --git a/src/Symfony/Components/Form/ChoiceField.php b/src/Symfony/Components/Form/ChoiceField.php index 91a1352824..80e5209e22 100644 --- a/src/Symfony/Components/Form/ChoiceField.php +++ b/src/Symfony/Components/Form/ChoiceField.php @@ -15,274 +15,237 @@ use Symfony\Components\Form\ValueTransformer\BooleanToStringTransformer; */ class ChoiceField extends HybridField { - /** - * Stores the preferred choices with the choices as keys - * @var array - */ - protected $preferredChoices = array(); + /** + * Stores the preferred choices with the choices as keys + * @var array + */ + protected $preferredChoices = array(); - /** - * {@inheritDoc} - */ - protected function configure() - { - $this->addRequiredOption('choices'); - $this->addOption('preferred_choices', array()); - $this->addOption('separator', '----------'); - $this->addOption('multiple', false); - $this->addOption('expanded', false); - $this->addOption('empty_value', ''); - $this->addOption('translate_choices', false); - - if (count($this->getOption('preferred_choices')) > 0) + /** + * {@inheritDoc} + */ + protected function configure() { - $this->preferredChoices = array_flip($this->getOption('preferred_choices')); + $this->addRequiredOption('choices'); + $this->addOption('preferred_choices', array()); + $this->addOption('separator', '----------'); + $this->addOption('multiple', false); + $this->addOption('expanded', false); + $this->addOption('empty_value', ''); + $this->addOption('translate_choices', false); - if (false && $diff = array_diff_key($this->options, $this->knownOptions)) - { - //throw new InvalidOptionsException(sprintf('%s does not support the following options: "%s".', get_class($this), implode('", "', array_keys($diff))), array_keys($diff)); - } - } + if (count($this->getOption('preferred_choices')) > 0) { + $this->preferredChoices = array_flip($this->getOption('preferred_choices')); - if ($this->getOption('expanded')) - { - $this->setFieldMode(self::GROUP); - - $choices = $this->getOption('choices'); - - foreach ($this->getOption('preferred_choices') as $choice) - { - $this->add($this->newChoiceField($choice, $choices[$choice])); - unset($choices[$choice]); - } - - foreach ($this->getOption('choices') as $choice => $value) - { - $this->add($this->newChoiceField($choice, $value)); - } - } - else - { - $this->setFieldMode(self::FIELD); - } - } - - /** - * Returns a new field of type radio button or checkbox. - * - * @param string $key The key for the option - * @param string $label The label for the option - */ - protected function newChoiceField($choice, $label) - { - if ($this->getOption('multiple')) - { - return new CheckboxField($choice, array( - 'value' => $choice, - 'label' => $label, - 'translate_label' => $this->getOption('translate_choices'), - )); - } - else - { - return new RadioField($choice, array( - 'value' => $choice, - 'label' => $label, - 'translate_label' => $this->getOption('translate_choices'), - )); - } - } - - /** - * {@inheritDoc} - * - * Takes care of converting the input from a single radio button - * to an array. - */ - public function bind($value) - { - if (!$this->getOption('multiple') && $this->getOption('expanded')) - { - $value = $value === null ? array() : array($value => true); - } - - parent::bind($value); - } - - /** - * Transforms a single choice or an array of choices to a format appropriate - * for the nested checkboxes/radio buttons. - * - * The result is an array with the options as keys and true/false as values, - * depending on whether a given option is selected. If this field is rendered - * as select tag, the value is not modified. - * - * @param mixed $value An array if "multiple" is set to true, a scalar - * value otherwise. - * @return mixed An array if "expanded" or "multiple" is set to true, - * a scalar value otherwise. - */ - protected function transform($value) - { - if ($this->getOption('expanded')) - { - $choices = $this->getOption('choices'); - - foreach ($choices as $choice => $_) - { - $choices[$choice] = $this->getOption('multiple') - ? in_array($choice, (array)$value, true) - : ($choice === $value); - } - - return $choices; - } - else - { - return parent::transform($value); - } - } - - /** - * Transforms a checkbox/radio button array to a single choice or an array - * of choices. - * - * The input value is an array with the choices as keys and true/false as - * values, depending on whether a given choice is selected. The output - * is an array with the selected choices or a single selected choice. - * - * @param mixed $value An array if "expanded" or "multiple" is set to true, - * a scalar value otherwise. - * @return mixed $value An array if "multiple" is set to true, a scalar - * value otherwise. - */ - protected function reverseTransform($value) - { - if ($this->getOption('expanded')) - { - $choices = array(); - - foreach ($value as $choice => $selected) - { - if ($selected) - { - $choices[] = $choice; - } - } - - if ($this->getOption('multiple')) - { - return $choices; - } - else - { - return count($choices) > 0 ? current($choices) : null; - } - } - else - { - return parent::reverseTransform($value); - } - } - - /** - * {@inheritDoc} - */ - public function render(array $attributes = array()) - { - if ($this->getOption('expanded')) - { - $html = ""; - - foreach ($this as $field) - { - $html .= $field->render()."\n"; - } - - return $html; - } - else - { - $attrs['id'] = $this->getId(); - $attrs['name'] = $this->getName(); - $attrs['disabled'] = $this->isDisabled(); - - // Add "[]" to the name in case a select tag with multiple options is - // displayed. Otherwise only one of the selected options is sent in the - // POST request. - if ($this->getOption('multiple') && !$this->getOption('expanded')) - { - $attrs['name'] .= '[]'; - } - - if ($this->getOption('multiple')) - { - $attrs['multiple'] = 'multiple'; - } - - $selected = array_flip(array_map('strval', (array)$this->getDisplayedData())); - $html = "\n"; - - if (!$this->isRequired()) - { - $html .= $this->renderChoices(array('' => $this->getOption('empty_value')), $selected)."\n"; - } - - $choices = $this->getOption('choices'); - - if (count($this->preferredChoices) > 0) - { - $html .= $this->renderChoices(array_intersect_key($choices, $this->preferredChoices), $selected)."\n"; - $html .= $this->generator->contentTag('option', $this->getOption('separator'), array('disabled' => true))."\n"; - } - - $html .= $this->renderChoices(array_diff_key($choices, $this->preferredChoices), $selected)."\n"; - - return $this->generator->contentTag('select', $html, array_merge($attrs, $attributes)); - } - } - - /** - * Returns an array of option tags for the choice field - * - * @return array An array of option tags - */ - protected function renderChoices(array $choices, array $selected) - { - $options = array(); - - foreach ($choices as $key => $option) - { - if (is_array($option)) - { - $options[] = $this->generator->contentTag( - 'optgroup', - "\n".$this->renderChoices($option, $selected)."\n", - array('label' => $this->generator->escape($key)) - ); - } - else - { - $attributes = array('value' => $this->generator->escape($key)); - - if (isset($selected[strval($key)])) - { - $attributes['selected'] = true; + if (false && $diff = array_diff_key($this->options, $this->knownOptions)) { + //throw new InvalidOptionsException(sprintf('%s does not support the following options: "%s".', get_class($this), implode('", "', array_keys($diff))), array_keys($diff)); + } } - if ($this->getOption('translate_choices')) - { - $option = $this->translate($option); - } + if ($this->getOption('expanded')) { + $this->setFieldMode(self::GROUP); - $options[] = $this->generator->contentTag( - 'option', - $this->generator->escape($option), - $attributes - ); - } + $choices = $this->getOption('choices'); + + foreach ($this->getOption('preferred_choices') as $choice) { + $this->add($this->newChoiceField($choice, $choices[$choice])); + unset($choices[$choice]); + } + + foreach ($this->getOption('choices') as $choice => $value) { + $this->add($this->newChoiceField($choice, $value)); + } + } else { + $this->setFieldMode(self::FIELD); + } } - return implode("\n", $options); - } + /** + * Returns a new field of type radio button or checkbox. + * + * @param string $key The key for the option + * @param string $label The label for the option + */ + protected function newChoiceField($choice, $label) + { + if ($this->getOption('multiple')) { + return new CheckboxField($choice, array( + 'value' => $choice, + 'label' => $label, + 'translate_label' => $this->getOption('translate_choices'), + )); + } else { + return new RadioField($choice, array( + 'value' => $choice, + 'label' => $label, + 'translate_label' => $this->getOption('translate_choices'), + )); + } + } + + /** + * {@inheritDoc} + * + * Takes care of converting the input from a single radio button + * to an array. + */ + public function bind($value) + { + if (!$this->getOption('multiple') && $this->getOption('expanded')) { + $value = $value === null ? array() : array($value => true); + } + + parent::bind($value); + } + + /** + * Transforms a single choice or an array of choices to a format appropriate + * for the nested checkboxes/radio buttons. + * + * The result is an array with the options as keys and true/false as values, + * depending on whether a given option is selected. If this field is rendered + * as select tag, the value is not modified. + * + * @param mixed $value An array if "multiple" is set to true, a scalar + * value otherwise. + * @return mixed An array if "expanded" or "multiple" is set to true, + * a scalar value otherwise. + */ + protected function transform($value) + { + if ($this->getOption('expanded')) { + $choices = $this->getOption('choices'); + + foreach ($choices as $choice => $_) { + $choices[$choice] = $this->getOption('multiple') + ? in_array($choice, (array)$value, true) + : ($choice === $value); + } + + return $choices; + } else { + return parent::transform($value); + } + } + + /** + * Transforms a checkbox/radio button array to a single choice or an array + * of choices. + * + * The input value is an array with the choices as keys and true/false as + * values, depending on whether a given choice is selected. The output + * is an array with the selected choices or a single selected choice. + * + * @param mixed $value An array if "expanded" or "multiple" is set to true, + * a scalar value otherwise. + * @return mixed $value An array if "multiple" is set to true, a scalar + * value otherwise. + */ + protected function reverseTransform($value) + { + if ($this->getOption('expanded')) { + $choices = array(); + + foreach ($value as $choice => $selected) { + if ($selected) { + $choices[] = $choice; + } + } + + if ($this->getOption('multiple')) { + return $choices; + } else { + return count($choices) > 0 ? current($choices) : null; + } + } else { + return parent::reverseTransform($value); + } + } + + /** + * {@inheritDoc} + */ + public function render(array $attributes = array()) + { + if ($this->getOption('expanded')) { + $html = ""; + + foreach ($this as $field) { + $html .= $field->render()."\n"; + } + + return $html; + } else { + $attrs['id'] = $this->getId(); + $attrs['name'] = $this->getName(); + $attrs['disabled'] = $this->isDisabled(); + + // Add "[]" to the name in case a select tag with multiple options is + // displayed. Otherwise only one of the selected options is sent in the + // POST request. + if ($this->getOption('multiple') && !$this->getOption('expanded')) { + $attrs['name'] .= '[]'; + } + + if ($this->getOption('multiple')) { + $attrs['multiple'] = 'multiple'; + } + + $selected = array_flip(array_map('strval', (array)$this->getDisplayedData())); + $html = "\n"; + + if (!$this->isRequired()) { + $html .= $this->renderChoices(array('' => $this->getOption('empty_value')), $selected)."\n"; + } + + $choices = $this->getOption('choices'); + + if (count($this->preferredChoices) > 0) { + $html .= $this->renderChoices(array_intersect_key($choices, $this->preferredChoices), $selected)."\n"; + $html .= $this->generator->contentTag('option', $this->getOption('separator'), array('disabled' => true))."\n"; + } + + $html .= $this->renderChoices(array_diff_key($choices, $this->preferredChoices), $selected)."\n"; + + return $this->generator->contentTag('select', $html, array_merge($attrs, $attributes)); + } + } + + /** + * Returns an array of option tags for the choice field + * + * @return array An array of option tags + */ + protected function renderChoices(array $choices, array $selected) + { + $options = array(); + + foreach ($choices as $key => $option) { + if (is_array($option)) { + $options[] = $this->generator->contentTag( + 'optgroup', + "\n".$this->renderChoices($option, $selected)."\n", + array('label' => $this->generator->escape($key)) + ); + } else { + $attributes = array('value' => $this->generator->escape($key)); + + if (isset($selected[strval($key)])) { + $attributes['selected'] = true; + } + + if ($this->getOption('translate_choices')) { + $option = $this->translate($option); + } + + $options[] = $this->generator->contentTag( + 'option', + $this->generator->escape($option), + $attributes + ); + } + } + + return implode("\n", $options); + } } diff --git a/src/Symfony/Components/Form/CollectionField.php b/src/Symfony/Components/Form/CollectionField.php index 76b7f71917..5d524949e3 100644 --- a/src/Symfony/Components/Form/CollectionField.php +++ b/src/Symfony/Components/Form/CollectionField.php @@ -21,83 +21,75 @@ use Symfony\Components\Form\Exception\UnexpectedTypeException; */ class CollectionField extends FieldGroup { - /** - * The prototype for the inner fields - * @var FieldInterface - */ - protected $prototype; + /** + * The prototype for the inner fields + * @var FieldInterface + */ + protected $prototype; - /** - * Repeats the given field twice to verify the user's input - * - * @param FieldInterface $innerField - */ - public function __construct(FieldInterface $innerField, array $options = array()) - { - $this->prototype = $innerField; - - parent::__construct($innerField->getKey(), $options); - } - - protected function configure() - { - $this->addOption('modifiable', false); - - if ($this->getOption('modifiable')) + /** + * Repeats the given field twice to verify the user's input + * + * @param FieldInterface $innerField + */ + public function __construct(FieldInterface $innerField, array $options = array()) { - $field = $this->newField('$$key$$', null); - // TESTME - $field->setRequired(false); - $this->add($field); - } - } + $this->prototype = $innerField; - public function setData($collection) - { - if (!is_array($collection) && !$collection instanceof Traversable) - { - throw new UnexpectedTypeException('The data must be an array'); + parent::__construct($innerField->getKey(), $options); } - foreach ($collection as $name => $value) + protected function configure() { - $this->add($this->newField($name, $name)); + $this->addOption('modifiable', false); + + if ($this->getOption('modifiable')) { + $field = $this->newField('$$key$$', null); + // TESTME + $field->setRequired(false); + $this->add($field); + } } - parent::setData($collection); - } - - public function bind($taintedData) - { - if (is_null($taintedData)) + public function setData($collection) { - $taintedData = array(); + if (!is_array($collection) && !$collection instanceof Traversable) { + throw new UnexpectedTypeException('The data must be an array'); + } + + foreach ($collection as $name => $value) { + $this->add($this->newField($name, $name)); + } + + parent::setData($collection); } - foreach ($this as $name => $field) + public function bind($taintedData) { - if (!isset($taintedData[$name]) && $this->getOption('modifiable') && $name != '$$key$$') - { - $this->remove($name); - } + if (is_null($taintedData)) { + $taintedData = array(); + } + + foreach ($this as $name => $field) { + if (!isset($taintedData[$name]) && $this->getOption('modifiable') && $name != '$$key$$') { + $this->remove($name); + } + } + + foreach ($taintedData as $name => $value) { + if (!isset($this[$name]) && $this->getOption('modifiable')) { + $this->add($this->newField($name, $name)); + } + } + + return parent::bind($taintedData); } - foreach ($taintedData as $name => $value) + protected function newField($key, $propertyPath) { - if (!isset($this[$name]) && $this->getOption('modifiable')) - { - $this->add($this->newField($name, $name)); - } + $field = clone $this->prototype; + $field->setKey($key); + $field->setPropertyPath($propertyPath === null ? null : '['.$propertyPath.']'); + return $field; } - - return parent::bind($taintedData); - } - - protected function newField($key, $propertyPath) - { - $field = clone $this->prototype; - $field->setKey($key); - $field->setPropertyPath($propertyPath === null ? null : '['.$propertyPath.']'); - return $field; - } } \ No newline at end of file diff --git a/src/Symfony/Components/Form/Configurable.php b/src/Symfony/Components/Form/Configurable.php index bba553c3c1..29eccd0d0b 100644 --- a/src/Symfony/Components/Form/Configurable.php +++ b/src/Symfony/Components/Form/Configurable.php @@ -19,124 +19,119 @@ use Symfony\Components\Form\Exception\InvalidOptionsException; */ abstract class Configurable { - /** - * The options and their values - * @var array - */ - private $options = array(); + /** + * The options and their values + * @var array + */ + private $options = array(); - /** - * The names of the valid options - * @var array - */ - private $knownOptions = array(); + /** + * The names of the valid options + * @var array + */ + private $knownOptions = array(); - /** - * The names of the required options - * @var array - */ - private $requiredOptions = array(); + /** + * The names of the required options + * @var array + */ + private $requiredOptions = array(); - /** - * The allowed values for each option - * @var array - */ - private $allowedValues = array(); + /** + * The allowed values for each option + * @var array + */ + private $allowedValues = array(); - /** - * Reads, validates and stores the given options - * - * @param array $options - */ - public function __construct(array $options = array()) - { - $this->options = array_merge($this->options, $options); - - $this->configure(); - - // check option names - if ($diff = array_diff_key($this->options, $this->knownOptions)) + /** + * Reads, validates and stores the given options + * + * @param array $options + */ + public function __construct(array $options = array()) { - throw new InvalidOptionsException(sprintf('%s does not support the following options: "%s".', get_class($this), implode('", "', array_keys($diff))), array_keys($diff)); + $this->options = array_merge($this->options, $options); + + $this->configure(); + + // check option names + if ($diff = array_diff_key($this->options, $this->knownOptions)) { + throw new InvalidOptionsException(sprintf('%s does not support the following options: "%s".', get_class($this), implode('", "', array_keys($diff))), array_keys($diff)); + } + + // check required options + if ($diff = array_diff_key($this->requiredOptions, $this->options)) { + throw new MissingOptionsException(sprintf('%s requires the following options: \'%s\'.', get_class($this), implode('", "', array_keys($diff))), array_keys($diff)); + } } - // check required options - if ($diff = array_diff_key($this->requiredOptions, $this->options)) + /** + * Configures the valid options + * + * This method should call addOption() or addRequiredOption() for every + * accepted option. + */ + protected function configure() { - throw new MissingOptionsException(sprintf('%s requires the following options: \'%s\'.', get_class($this), implode('", "', array_keys($diff))), array_keys($diff)); - } - } - - /** - * Configures the valid options - * - * This method should call addOption() or addRequiredOption() for every - * accepted option. - */ - protected function configure() - { - } - - /** - * Returns an option value. - * - * @param string $name The option name - * - * @return mixed The option value - */ - public function getOption($name) - { - return array_key_exists($name, $this->options) ? $this->options[$name] : null; - } - - /** - * Adds a new option value with a default value. - * - * @param string $name The option name - * @param mixed $value The default value - */ - protected function addOption($name, $value = null, array $allowedValues = array()) - { - $this->knownOptions[$name] = true; - - if (!array_key_exists($name, $this->options)) - { - $this->options[$name] = $value; } - if (count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues)) + /** + * Returns an option value. + * + * @param string $name The option name + * + * @return mixed The option value + */ + public function getOption($name) { - throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name)); + return array_key_exists($name, $this->options) ? $this->options[$name] : null; } - } - /** - * Adds a required option. - * - * @param string $name The option name - */ - protected function addRequiredOption($name, array $allowedValues = array()) - { - $this->knownOptions[$name] = true; - $this->requiredOptions[$name] = true; - - // only test if the option is set, otherwise an error will be thrown - // anyway - if (isset($this->options[$name]) && count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues)) + /** + * Adds a new option value with a default value. + * + * @param string $name The option name + * @param mixed $value The default value + */ + protected function addOption($name, $value = null, array $allowedValues = array()) { - throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name)); - } - } + $this->knownOptions[$name] = true; - /** - * Returns true if the option exists. - * - * @param string $name The option name - * - * @return bool true if the option is set, false otherwise - */ - public function hasOption($name) - { - return isset($this->options[$name]); - } + if (!array_key_exists($name, $this->options)) { + $this->options[$name] = $value; + } + + if (count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues)) { + throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name)); + } + } + + /** + * Adds a required option. + * + * @param string $name The option name + */ + protected function addRequiredOption($name, array $allowedValues = array()) + { + $this->knownOptions[$name] = true; + $this->requiredOptions[$name] = true; + + // only test if the option is set, otherwise an error will be thrown + // anyway + if (isset($this->options[$name]) && count($allowedValues) > 0 && !in_array($this->options[$name], $allowedValues)) { + throw new InvalidOptionsException(sprintf('The option "%s" is expected to be one of "%s", but is "%s"', $name, implode('", "', $allowedValues), $this->options[$name]), array($name)); + } + } + + /** + * Returns true if the option exists. + * + * @param string $name The option name + * + * @return bool true if the option is set, false otherwise + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } } diff --git a/src/Symfony/Components/Form/Configurator/ConfiguratorInterface.php b/src/Symfony/Components/Form/Configurator/ConfiguratorInterface.php index 88c8f12943..bb3d6e9f48 100644 --- a/src/Symfony/Components/Form/Configurator/ConfiguratorInterface.php +++ b/src/Symfony/Components/Form/Configurator/ConfiguratorInterface.php @@ -4,11 +4,11 @@ namespace Symfony\Components\Form\Configurator; interface ConfiguratorInterface { - public function initialize($object); + public function initialize($object); - public function getClass($fieldName); + public function getClass($fieldName); - public function getOptions($fieldName); + public function getOptions($fieldName); - public function isRequired($fieldName); + public function isRequired($fieldName); } \ No newline at end of file diff --git a/src/Symfony/Components/Form/Configurator/ValidatorConfigurator.php b/src/Symfony/Components/Form/Configurator/ValidatorConfigurator.php index 0effe49033..6a9bb9f1a0 100644 --- a/src/Symfony/Components/Form/Configurator/ValidatorConfigurator.php +++ b/src/Symfony/Components/Form/Configurator/ValidatorConfigurator.php @@ -4,32 +4,32 @@ namespace Symfony\Components\Form\Configurator; class ValidatorConfigurator implements ConfiguratorInterface { - protected $metaData = null; - protected $classMetaData = null; + protected $metaData = null; + protected $classMetaData = null; - public function __construct(MetaDataInterface $metaData) - { - $this->metaData = $metaData; - } + public function __construct(MetaDataInterface $metaData) + { + $this->metaData = $metaData; + } - public function initialize($object) - { - $this->classMetaData = $this->metaData->getClassMetaData(get_class($object)); - } + public function initialize($object) + { + $this->classMetaData = $this->metaData->getClassMetaData(get_class($object)); + } - public function getClass($fieldName) - { + public function getClass($fieldName) + { - } + } - public function getOptions($fieldName) - { + public function getOptions($fieldName) + { - } + } - public function isRequired($fieldName) - { - return $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotNull') - || $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotEmpty'); - } + public function isRequired($fieldName) + { + return $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotNull') + || $this->classMetaData->getPropertyMetaData($fieldName)->hasConstraint('NotEmpty'); + } } \ No newline at end of file diff --git a/src/Symfony/Components/Form/DateField.php b/src/Symfony/Components/Form/DateField.php index a0122ea0a8..f2a6e9d8ee 100644 --- a/src/Symfony/Components/Form/DateField.php +++ b/src/Symfony/Components/Form/DateField.php @@ -19,244 +19,224 @@ use Symfony\Components\Form\ValueTransformer\DateTimeToArrayTransformer; class DateField extends HybridField { - const FULL = 'full'; - const LONG = 'long'; - const MEDIUM = 'medium'; - const SHORT = 'short'; + const FULL = 'full'; + const LONG = 'long'; + const MEDIUM = 'medium'; + const SHORT = 'short'; - const DATETIME = 'datetime'; - const STRING = 'string'; - const TIMESTAMP = 'timestamp'; - const RAW = 'raw'; + const DATETIME = 'datetime'; + const STRING = 'string'; + const TIMESTAMP = 'timestamp'; + const RAW = 'raw'; - const INPUT = 'input'; - const CHOICE = 'choice'; + const INPUT = 'input'; + const CHOICE = 'choice'; - protected static $formats = array( - self::FULL, - self::LONG, - self::MEDIUM, - self::SHORT, - ); - - protected static $intlFormats = array( - self::FULL => \IntlDateFormatter::FULL, - self::LONG => \IntlDateFormatter::LONG, - self::MEDIUM => \IntlDateFormatter::MEDIUM, - self::SHORT => \IntlDateFormatter::SHORT, - ); - - protected static $widgets = array( - self::INPUT, - self::CHOICE, - ); - - protected static $types = array( - self::DATETIME, - self::STRING, - self::TIMESTAMP, - self::RAW, - ); - - /** - * The ICU formatter instance - * @var \IntlDateFormatter - */ - protected $formatter; - - /** - * Configures the text field. - * - * Available options: - * - * * widget: How to render the field ("input" or "select"). Default: "input" - * * years: An array of years for the year select tag (optional) - * * months: An array of months for the month select tag (optional) - * * days: An array of days for the day select tag (optional) - * * format: See DateValueTransformer. Default: medium - * * type: The type of the date ("date", "datetime" or "timestamp"). Default: "date" - * * data_timezone: The timezone of the data - * * user_timezone: The timezone of the user entering a new value - * * pattern: The pattern for the select boxes when "widget" is "select". - * You can use the placeholders "%year%", "%month%" and "%day%". - * Default: locale dependent - * - * @param array $options Options for this field - * @throws \InvalidArgumentException Thrown if you want to show a timestamp with the select widget. - */ - protected function configure() - { - $this->addOption('years', range(date('Y') - 5, date('Y') + 5)); - $this->addOption('months', range(1, 12)); - $this->addOption('days', range(1, 31)); - $this->addOption('format', self::MEDIUM, self::$formats); - $this->addOption('type', self::DATETIME, self::$types); - $this->addOption('data_timezone', 'UTC'); - $this->addOption('user_timezone', 'UTC'); - $this->addOption('widget', self::CHOICE, self::$widgets); - $this->addOption('pattern'); - - $this->formatter = new \IntlDateFormatter( - $this->locale, - self::$intlFormats[$this->getOption('format')], - \IntlDateFormatter::NONE + protected static $formats = array( + self::FULL, + self::LONG, + self::MEDIUM, + self::SHORT, ); - $transformers = array(); + protected static $intlFormats = array( + self::FULL => \IntlDateFormatter::FULL, + self::LONG => \IntlDateFormatter::LONG, + self::MEDIUM => \IntlDateFormatter::MEDIUM, + self::SHORT => \IntlDateFormatter::SHORT, + ); - if ($this->getOption('type') === self::STRING) + protected static $widgets = array( + self::INPUT, + self::CHOICE, + ); + + protected static $types = array( + self::DATETIME, + self::STRING, + self::TIMESTAMP, + self::RAW, + ); + + /** + * The ICU formatter instance + * @var \IntlDateFormatter + */ + protected $formatter; + + /** + * Configures the text field. + * + * Available options: + * + * * widget: How to render the field ("input" or "select"). Default: "input" + * * years: An array of years for the year select tag (optional) + * * months: An array of months for the month select tag (optional) + * * days: An array of days for the day select tag (optional) + * * format: See DateValueTransformer. Default: medium + * * type: The type of the date ("date", "datetime" or "timestamp"). Default: "date" + * * data_timezone: The timezone of the data + * * user_timezone: The timezone of the user entering a new value + * * pattern: The pattern for the select boxes when "widget" is "select". + * You can use the placeholders "%year%", "%month%" and "%day%". + * Default: locale dependent + * + * @param array $options Options for this field + * @throws \InvalidArgumentException Thrown if you want to show a timestamp with the select widget. + */ + protected function configure() { - $transformers[] = new StringToDateTimeTransformer(array( - 'input_timezone' => $this->getOption('data_timezone'), - 'output_timezone' => $this->getOption('data_timezone'), - 'format' => 'Y-m-d', - )); - } - else if ($this->getOption('type') === self::TIMESTAMP) - { - $transformers[] = new TimestampToDateTimeTransformer(array( - 'output_timezone' => $this->getOption('data_timezone'), - 'input_timezone' => $this->getOption('data_timezone'), - )); - } - else if ($this->getOption('type') === self::RAW) - { - $transformers[] = new ReversedTransformer(new DateTimeToArrayTransformer(array( - 'input_timezone' => $this->getOption('data_timezone'), - 'output_timezone' => $this->getOption('data_timezone'), - 'fields' => array('year', 'month', 'day'), - ))); + $this->addOption('years', range(date('Y') - 5, date('Y') + 5)); + $this->addOption('months', range(1, 12)); + $this->addOption('days', range(1, 31)); + $this->addOption('format', self::MEDIUM, self::$formats); + $this->addOption('type', self::DATETIME, self::$types); + $this->addOption('data_timezone', 'UTC'); + $this->addOption('user_timezone', 'UTC'); + $this->addOption('widget', self::CHOICE, self::$widgets); + $this->addOption('pattern'); + + $this->formatter = new \IntlDateFormatter( + $this->locale, + self::$intlFormats[$this->getOption('format')], + \IntlDateFormatter::NONE + ); + + $transformers = array(); + + if ($this->getOption('type') === self::STRING) { + $transformers[] = new StringToDateTimeTransformer(array( + 'input_timezone' => $this->getOption('data_timezone'), + 'output_timezone' => $this->getOption('data_timezone'), + 'format' => 'Y-m-d', + )); + } else if ($this->getOption('type') === self::TIMESTAMP) { + $transformers[] = new TimestampToDateTimeTransformer(array( + 'output_timezone' => $this->getOption('data_timezone'), + 'input_timezone' => $this->getOption('data_timezone'), + )); + } else if ($this->getOption('type') === self::RAW) { + $transformers[] = new ReversedTransformer(new DateTimeToArrayTransformer(array( + 'input_timezone' => $this->getOption('data_timezone'), + 'output_timezone' => $this->getOption('data_timezone'), + 'fields' => array('year', 'month', 'day'), + ))); + } + + if ($this->getOption('widget') === self::INPUT) { + $transformers[] = new DateTimeToLocalizedStringTransformer(array( + 'date_format' => $this->getOption('format'), + 'time_format' => DateTimeToLocalizedStringTransformer::NONE, + 'input_timezone' => $this->getOption('data_timezone'), + 'output_timezone' => $this->getOption('user_timezone'), + )); + + $this->setFieldMode(self::FIELD); + } else { + $transformers[] = new DateTimeToArrayTransformer(array( + 'input_timezone' => $this->getOption('data_timezone'), + 'output_timezone' => $this->getOption('user_timezone'), + )); + + $this->setFieldMode(self::GROUP); + + $this->add(new ChoiceField('year', array( + 'choices' => $this->generatePaddedChoices($this->getOption('years'), 4), + ))); + $this->add(new ChoiceField('month', array( + 'choices' => $this->generateMonthChoices($this->getOption('months')), + ))); + $this->add(new ChoiceField('day', array( + 'choices' => $this->generatePaddedChoices($this->getOption('days'), 2), + ))); + } + + if (count($transformers) > 0) { + $this->setValueTransformer(new ValueTransformerChain($transformers)); + } } - if ($this->getOption('widget') === self::INPUT) + /** + * Generates an array of choices for the given values + * + * If the values are shorter than $padLength characters, they are padded with + * zeros on the left side. + * + * @param array $values The available choices + * @param integer $padLength The length to pad the choices + * @return array An array with the input values as keys and the + * padded values as values + */ + protected function generatePaddedChoices(array $values, $padLength) { - $transformers[] = new DateTimeToLocalizedStringTransformer(array( - 'date_format' => $this->getOption('format'), - 'time_format' => DateTimeToLocalizedStringTransformer::NONE, - 'input_timezone' => $this->getOption('data_timezone'), - 'output_timezone' => $this->getOption('user_timezone'), - )); + $choices = array(); - $this->setFieldMode(self::FIELD); - } - else - { - $transformers[] = new DateTimeToArrayTransformer(array( - 'input_timezone' => $this->getOption('data_timezone'), - 'output_timezone' => $this->getOption('user_timezone'), - )); + foreach ($values as $value) { + $choices[$value] = str_pad($value, $padLength, '0', STR_PAD_LEFT); + } - $this->setFieldMode(self::GROUP); - - $this->add(new ChoiceField('year', array( - 'choices' => $this->generatePaddedChoices($this->getOption('years'), 4), - ))); - $this->add(new ChoiceField('month', array( - 'choices' => $this->generateMonthChoices($this->getOption('months')), - ))); - $this->add(new ChoiceField('day', array( - 'choices' => $this->generatePaddedChoices($this->getOption('days'), 2), - ))); + return $choices; } - if (count($transformers) > 0) + /** + * Generates an array of localized month choices + * + * @param array $months The month numbers to generate + * @return array The localized months respecting the configured + * locale and date format + */ + protected function generateMonthChoices(array $months) { - $this->setValueTransformer(new ValueTransformerChain($transformers)); - } - } + $pattern = $this->formatter->getPattern(); - /** - * Generates an array of choices for the given values - * - * If the values are shorter than $padLength characters, they are padded with - * zeros on the left side. - * - * @param array $values The available choices - * @param integer $padLength The length to pad the choices - * @return array An array with the input values as keys and the - * padded values as values - */ - protected function generatePaddedChoices(array $values, $padLength) - { - $choices = array(); + if (preg_match('/M+/', $pattern, $matches)) { + $this->formatter->setPattern($matches[0]); + $choices = array(); - foreach ($values as $value) - { - $choices[$value] = str_pad($value, $padLength, '0', STR_PAD_LEFT); + foreach ($months as $month) { + $choices[$month] = $this->formatter->format(gmmktime(0, 0, 0, $month)); + } + + $this->formatter->setPattern($pattern); + } else { + $choices = $this->generatePaddedChoices($months, 2); + } + + return $choices; } - return $choices; - } - - /** - * Generates an array of localized month choices - * - * @param array $months The month numbers to generate - * @return array The localized months respecting the configured - * locale and date format - */ - protected function generateMonthChoices(array $months) - { - $pattern = $this->formatter->getPattern(); - - if (preg_match('/M+/', $pattern, $matches)) + /** + * {@inheritDoc} + */ + public function render(array $attributes = array()) { - $this->formatter->setPattern($matches[0]); - $choices = array(); + if ($this->getOption('widget') === self::INPUT) { + return $this->generator->tag('input', array_merge(array( + 'id' => $this->getId(), + 'name' => $this->getName(), + 'value' => $this->getDisplayedData(), + 'type' => 'text', + ), $attributes)); + } else { + // set order as specified in the pattern + if ($this->getOption('pattern')) { + $pattern = $this->getOption('pattern'); + } + // set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy) + // lookup various formats at http://userguide.icu-project.org/formatparse/datetime + else if (preg_match('/^([yMd]+).+([yMd]+).+([yMd]+)$/', $this->formatter->getPattern())) { + $pattern = preg_replace(array('/y+/', '/M+/', '/d+/'), array('%year%', '%month%', '%day%'), $this->formatter->getPattern()); + } + // default fallback + else { + $pattern = '%year%-%month%-%day%'; + } - foreach ($months as $month) - { - $choices[$month] = $this->formatter->format(gmmktime(0, 0, 0, $month)); - } - - $this->formatter->setPattern($pattern); + return str_replace(array('%year%', '%month%', '%day%'), array( + $this->get('year')->render($attributes), + $this->get('month')->render($attributes), + $this->get('day')->render($attributes), + ), $pattern); + } } - else - { - $choices = $this->generatePaddedChoices($months, 2); - } - - return $choices; - } - - /** - * {@inheritDoc} - */ - public function render(array $attributes = array()) - { - if ($this->getOption('widget') === self::INPUT) - { - return $this->generator->tag('input', array_merge(array( - 'id' => $this->getId(), - 'name' => $this->getName(), - 'value' => $this->getDisplayedData(), - 'type' => 'text', - ), $attributes)); - } - else - { - // set order as specified in the pattern - if ($this->getOption('pattern')) - { - $pattern = $this->getOption('pattern'); - } - // set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy) - // lookup various formats at http://userguide.icu-project.org/formatparse/datetime - else if (preg_match('/^([yMd]+).+([yMd]+).+([yMd]+)$/', $this->formatter->getPattern())) - { - $pattern = preg_replace(array('/y+/', '/M+/', '/d+/'), array('%year%', '%month%', '%day%'), $this->formatter->getPattern()); - } - // default fallback - else - { - $pattern = '%year%-%month%-%day%'; - } - - return str_replace(array('%year%', '%month%', '%day%'), array( - $this->get('year')->render($attributes), - $this->get('month')->render($attributes), - $this->get('day')->render($attributes), - ), $pattern); - } - } } \ No newline at end of file diff --git a/src/Symfony/Components/Form/DateTimeField.php b/src/Symfony/Components/Form/DateTimeField.php index 675b8b9da8..a1a50b18d3 100644 --- a/src/Symfony/Components/Form/DateTimeField.php +++ b/src/Symfony/Components/Form/DateTimeField.php @@ -14,115 +14,112 @@ use Symfony\Components\Form\ValueTransformer\ValueTransformerChain; */ class DateTimeField extends FieldGroup { - const DATETIME = 'datetime'; - const STRING = 'string'; - const TIMESTAMP = 'timestamp'; + const DATETIME = 'datetime'; + const STRING = 'string'; + const TIMESTAMP = 'timestamp'; - protected static $types = array( - self::DATETIME, - self::STRING, - self::TIMESTAMP, - ); + protected static $types = array( + self::DATETIME, + self::STRING, + self::TIMESTAMP, + ); - protected static $dateWidgets = array( - DateField::CHOICE, - DateField::INPUT, - ); + protected static $dateWidgets = array( + DateField::CHOICE, + DateField::INPUT, + ); - protected static $timeWidgets = array( - TimeField::CHOICE, - TimeField::INPUT, - ); + protected static $timeWidgets = array( + TimeField::CHOICE, + TimeField::INPUT, + ); - /** - * {@inheritDoc} - */ - public function configure() - { - $this->addOption('years', range(date('Y') - 5, date('Y') + 5)); - $this->addOption('months', range(1, 12)); - $this->addOption('days', range(1, 31)); - $this->addOption('hours', range(0, 23)); - $this->addOption('minutes', range(0, 59)); - $this->addOption('seconds', range(0, 59)); - $this->addOption('data_timezone', 'UTC'); - $this->addOption('user_timezone', 'UTC'); - $this->addOption('date_widget', DateField::INPUT, self::$dateWidgets); - $this->addOption('time_widget', TimeField::CHOICE, self::$timeWidgets); - $this->addOption('type', self::DATETIME, self::$types); - $this->addOption('with_seconds', false); - - $this->add(new DateField('date', array( - 'type' => DateField::RAW, - 'widget' => $this->getOption('date_widget'), - 'data_timezone' => $this->getOption('user_timezone'), - 'user_timezone' => $this->getOption('user_timezone'), - 'years' => $this->getOption('years'), - 'months' => $this->getOption('months'), - 'days' => $this->getOption('days'), - ))); - $this->add(new TimeField('time', array( - 'type' => TimeField::RAW, - 'widget' => $this->getOption('time_widget'), - 'data_timezone' => $this->getOption('user_timezone'), - 'user_timezone' => $this->getOption('user_timezone'), - 'with_seconds' => $this->getOption('with_seconds'), - 'hours' => $this->getOption('hours'), - 'minutes' => $this->getOption('minutes'), - 'seconds' => $this->getOption('seconds'), - ))); - - $transformers = array(); - - if ($this->getOption('type') == self::STRING) + /** + * {@inheritDoc} + */ + public function configure() { - $transformers[] = new StringToDateTimeTransformer(array( - 'input_timezone' => $this->getOption('data_timezone'), - 'output_timezone' => $this->getOption('data_timezone'), - )); - } - else if ($this->getOption('type') == self::TIMESTAMP) - { - $transformers[] = new TimestampToDateTimeTransformer(array( - 'input_timezone' => $this->getOption('data_timezone'), - 'output_timezone' => $this->getOption('data_timezone'), - )); + $this->addOption('years', range(date('Y') - 5, date('Y') + 5)); + $this->addOption('months', range(1, 12)); + $this->addOption('days', range(1, 31)); + $this->addOption('hours', range(0, 23)); + $this->addOption('minutes', range(0, 59)); + $this->addOption('seconds', range(0, 59)); + $this->addOption('data_timezone', 'UTC'); + $this->addOption('user_timezone', 'UTC'); + $this->addOption('date_widget', DateField::INPUT, self::$dateWidgets); + $this->addOption('time_widget', TimeField::CHOICE, self::$timeWidgets); + $this->addOption('type', self::DATETIME, self::$types); + $this->addOption('with_seconds', false); + + $this->add(new DateField('date', array( + 'type' => DateField::RAW, + 'widget' => $this->getOption('date_widget'), + 'data_timezone' => $this->getOption('user_timezone'), + 'user_timezone' => $this->getOption('user_timezone'), + 'years' => $this->getOption('years'), + 'months' => $this->getOption('months'), + 'days' => $this->getOption('days'), + ))); + $this->add(new TimeField('time', array( + 'type' => TimeField::RAW, + 'widget' => $this->getOption('time_widget'), + 'data_timezone' => $this->getOption('user_timezone'), + 'user_timezone' => $this->getOption('user_timezone'), + 'with_seconds' => $this->getOption('with_seconds'), + 'hours' => $this->getOption('hours'), + 'minutes' => $this->getOption('minutes'), + 'seconds' => $this->getOption('seconds'), + ))); + + $transformers = array(); + + if ($this->getOption('type') == self::STRING) { + $transformers[] = new StringToDateTimeTransformer(array( + 'input_timezone' => $this->getOption('data_timezone'), + 'output_timezone' => $this->getOption('data_timezone'), + )); + } else if ($this->getOption('type') == self::TIMESTAMP) { + $transformers[] = new TimestampToDateTimeTransformer(array( + 'input_timezone' => $this->getOption('data_timezone'), + 'output_timezone' => $this->getOption('data_timezone'), + )); + } + + $transformers[] = new DateTimeToArrayTransformer(array( + 'input_timezone' => $this->getOption('data_timezone'), + 'output_timezone' => $this->getOption('user_timezone'), + )); + + $this->setValueTransformer(new ValueTransformerChain($transformers)); } - $transformers[] = new DateTimeToArrayTransformer(array( - 'input_timezone' => $this->getOption('data_timezone'), - 'output_timezone' => $this->getOption('user_timezone'), - )); + /** + * {@inheritDoc} + */ + protected function transform($value) + { + $value = parent::transform($value); - $this->setValueTransformer(new ValueTransformerChain($transformers)); - } + return array('date' => $value, 'time' => $value); + } - /** - * {@inheritDoc} - */ - protected function transform($value) - { - $value = parent::transform($value); + /** + * {@inheritDoc} + */ + protected function reverseTransform($value) + { + return parent::reverseTransform(array_merge($value['date'], $value['time'])); + } - return array('date' => $value, 'time' => $value); - } + /** + * {@inheritDoc} + */ + public function render(array $attributes = array()) + { + $html = $this->get('date')->render($attributes)."\n"; + $html .= $this->get('time')->render($attributes); - /** - * {@inheritDoc} - */ - protected function reverseTransform($value) - { - return parent::reverseTransform(array_merge($value['date'], $value['time'])); - } - - /** - * {@inheritDoc} - */ - public function render(array $attributes = array()) - { - $html = $this->get('date')->render($attributes)."\n"; - $html .= $this->get('time')->render($attributes); - - return $html; - } + return $html; + } } diff --git a/src/Symfony/Components/Form/Exception/InvalidOptionsException.php b/src/Symfony/Components/Form/Exception/InvalidOptionsException.php index 5cbfa166e3..1baf91a1da 100644 --- a/src/Symfony/Components/Form/Exception/InvalidOptionsException.php +++ b/src/Symfony/Components/Form/Exception/InvalidOptionsException.php @@ -4,17 +4,17 @@ namespace Symfony\Components\Form\Exception; class InvalidOptionsException extends FormException { - private $options; + private $options; - public function __construct($message, array $options) - { - parent::__construct($message); + public function __construct($message, array $options) + { + parent::__construct($message); - $this->options = $options; - } + $this->options = $options; + } - public function getOptions() - { - return $this->options; - } + public function getOptions() + { + return $this->options; + } } \ No newline at end of file diff --git a/src/Symfony/Components/Form/Exception/MissingOptionsException.php b/src/Symfony/Components/Form/Exception/MissingOptionsException.php index a5a6decd5a..e0ef3259d7 100644 --- a/src/Symfony/Components/Form/Exception/MissingOptionsException.php +++ b/src/Symfony/Components/Form/Exception/MissingOptionsException.php @@ -4,17 +4,17 @@ namespace Symfony\Components\Form\Exception; class MissingOptionsException extends FormException { - private $options; + private $options; - public function __construct($message, array $options) - { - parent::__construct($message); + public function __construct($message, array $options) + { + parent::__construct($message); - $this->options = $options; - } + $this->options = $options; + } - public function getOptions() - { - return $this->options; - } + public function getOptions() + { + return $this->options; + } } \ No newline at end of file diff --git a/src/Symfony/Components/Form/Field.php b/src/Symfony/Components/Form/Field.php index dffa3c029a..2467a9558e 100644 --- a/src/Symfony/Components/Form/Field.php +++ b/src/Symfony/Components/Form/Field.php @@ -13,693 +13,623 @@ use Symfony\Components\I18N\TranslatorInterface; abstract class Field extends Configurable implements FieldInterface { - /** - * The object used for generating HTML code - * @var HtmlGeneratorInterface - */ - protected $generator = null; + /** + * The object used for generating HTML code + * @var HtmlGeneratorInterface + */ + protected $generator = null; - protected $taintedData = null; - protected $locale = null; - protected $translator = null; + protected $taintedData = null; + protected $locale = null; + protected $translator = null; - private $errors = array(); - private $key = ''; - private $parent = null; - private $renderer = null; - private $bound = false; - private $required = null; - private $data = null; - private $transformedData = null; - private $valueTransformer = null; - private $propertyPath = null; + private $errors = array(); + private $key = ''; + private $parent = null; + private $renderer = null; + private $bound = false; + private $required = null; + private $data = null; + private $transformedData = null; + private $valueTransformer = null; + private $propertyPath = null; - public function __construct($key, array $options = array()) - { - $this->addOption('trim', true); - $this->addOption('required', true); - $this->addOption('disabled', false); - $this->addOption('property_path', (string)$key); - - $this->key = (string)$key; - $this->locale = \Locale::getDefault(); - $this->generator = new HtmlGenerator(); - - parent::__construct($options); - - $this->transformedData = $this->transform($this->data); - $this->required = $this->getOption('required'); - - $this->setPropertyPath($this->getOption('property_path')); - } - - /** - * Clones this field. - */ - public function __clone() - { - // TODO - } - - /** - * Returns the data of the field as it is displayed to the user. - * - * @return string|array When the field is not bound, the transformed - * default data is returned. When the field is bound, - * the bound data is returned. - */ - public function getDisplayedData() - { - return $this->getTransformedData(); - } - - /** - * Returns the data transformed by the value transformer - * - * @return string - */ - protected function getTransformedData() - { - return $this->transformedData; - } - - /** - * {@inheritDoc} - */ - public function setPropertyPath($propertyPath) - { - $this->propertyPath = empty($propertyPath) ? null : new PropertyPath($propertyPath); - } - - /** - * {@inheritDoc} - */ - public function getPropertyPath() - { - return $this->propertyPath; - } - - /** - * {@inheritDoc} - */ - public function setKey($key) - { - $this->key = (string)$key; - } - - /** - * {@inheritDoc} - */ - public function getKey() - { - return $this->key; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return is_null($this->parent) ? $this->key : $this->parent->getName().'['.$this->key.']'; - } - - /** - * {@inheritDoc} - */ - public function getId() - { - return is_null($this->parent) ? $this->key : $this->parent->getId().'_'.$this->key; - } - - /** - * {@inheritDoc} - */ - public function setRequired($required) - { - $this->required = $required; - } - - /** - * {@inheritDoc} - */ - public function isRequired() - { - if (is_null($this->parent) || $this->parent->isRequired()) + public function __construct($key, array $options = array()) { - return $this->required; - } - else - { - return false; - } - } + $this->addOption('trim', true); + $this->addOption('required', true); + $this->addOption('disabled', false); + $this->addOption('property_path', (string)$key); - /** - * {@inheritDoc} - */ - public function isDisabled() - { - if (is_null($this->parent) || !$this->parent->isDisabled()) - { - return $this->getOption('disabled'); - } - else - { - return true; - } - } + $this->key = (string)$key; + $this->locale = \Locale::getDefault(); + $this->generator = new HtmlGenerator(); - /** - * {@inheritDoc} - */ - public function setGenerator(HtmlGeneratorInterface $generator) - { - $this->generator = $generator; - } + parent::__construct($options); - /** - * {@inheritDoc} - */ - public function isMultipart() - { - return false; - } + $this->transformedData = $this->transform($this->data); + $this->required = $this->getOption('required'); - /** - * Returns true if the widget is hidden. - * - * @return Boolean true if the widget is hidden, false otherwise - */ - public function isHidden() - { - return false; - } - - /** - * {@inheritDoc} - */ - public function setParent(FieldInterface $parent = null) - { - $this->parent = $parent; - } - - /** - * Returns the parent field. - * - * @return FieldInterface The parent field - */ - public function getParent() - { - return $this->parent; - } - - /** - * Updates the field with default data - * - * @see FieldInterface - */ - public function setData($data) - { - $this->data = $data; - $this->transformedData = $this->transform($data); - } - - /** - * Binds POST data to the field, transforms and validates it. - * - * @param string|array $taintedData The POST data - * @return boolean Whether the form is valid - * @throws AlreadyBoundException when the field is already bound - */ - public function bind($taintedData) - { - $this->transformedData = is_array($taintedData) || is_object($taintedData) ? $taintedData : (string)$taintedData; - $this->bound = true; - $this->errors = array(); - - if (is_string($this->transformedData) && $this->getOption('trim')) - { - $this->transformedData = trim($this->transformedData); + $this->setPropertyPath($this->getOption('property_path')); } - try + /** + * Clones this field. + */ + public function __clone() { - $this->data = $this->processData($data = $this->reverseTransform($this->transformedData)); - $this->transformedData = $this->transform($this->data); - } - catch (TransformationFailedException $e) - { - // TODO better text - // TESTME - $this->addError('invalid (localized)'); - } - } - - /** - * Processes the bound reverse-transformed data. - * - * This method can be overridden if you want to modify the data entered - * by the user. Note that the data is already in reverse transformed format. - * - * This method will not be called if reverse transformation fails. - * - * @param mixed $data - * @return mixed - */ - protected function processData($data) - { - return $data; - } - - /** - * Returns the normalized data of the field. - * - * @return mixed When the field is not bound, the default data is returned. - * When the field is bound, the normalized bound data is - * returned if the field is valid, null otherwise. - */ - public function getData() - { - return $this->data; - } - - /** - * Adds an error to the field. - * - * @see FieldInterface - */ - public function addError($message, PropertyPath $path = null, $type = null) - { - $this->errors[] = $message; - } - - /** - * Returns whether the field is bound. - * - * @return boolean true if the form is bound to input values, false otherwise - */ - public function isBound() - { - return $this->bound; - } - - /** - * Returns whether the field is valid. - * - * @return boolean - */ - public function isValid() - { - return $this->isBound() ? count($this->errors)==0 : false; // TESTME - } - - /** - * Returns weather there are errors. - * - * @return boolean true if form is bound and not valid - */ - public function hasErrors() - { - return $this->isBound() && !$this->isValid(); - } - - /** - * Returns all errors - * - * @return array An array of errors that occured during binding - */ - public function getErrors() - { - return $this->errors; - } - - /** - * Sets the locale of this field. - * - * @see Localizable - */ - public function setLocale($locale) - { - $this->locale = $locale; - - if ($this->valueTransformer !== null && $this->valueTransformer instanceof Localizable) - { - $this->valueTransformer->setLocale($locale); - } - } - - /** - * Sets the translator of this field. - * - * @see Translatable - */ - public function setTranslator(TranslatorInterface $translator) - { - $this->translator = $translator; - - if ($this->valueTransformer !== null && $this->valueTransformer instanceof Translatable) - { - $this->valueTransformer->setTranslator($translator); - } - } - - /** - * Translates the text using the associated translator, if available - * - * If no translator is available, the original text is returned without - * modification. - * - * @param string $text The text to translate - * @param array $parameters The parameters to insert in the text - * @return string The translated text - */ - protected function translate($text, array $parameters = array()) - { - if ($this->translator !== null) - { - $text = $this->translator->translate($text, $parameters); + // TODO } - return $text; - } - - /** - * Injects the locale and the translator into the given object, if set. - * - * The locale is injected only if the object implements Localizable. The - * translator is injected only if the object implements Translatable. - * - * @param object $object - */ - protected function injectLocaleAndTranslator($object) - { - if ($object instanceof Localizable) + /** + * Returns the data of the field as it is displayed to the user. + * + * @return string|array When the field is not bound, the transformed + * default data is returned. When the field is bound, + * the bound data is returned. + */ + public function getDisplayedData() { - $object->setLocale($this->locale); + return $this->getTransformedData(); } - if (!is_null($this->translator) && $object instanceof Translatable) + /** + * Returns the data transformed by the value transformer + * + * @return string + */ + protected function getTransformedData() { - $object->setTranslator($this->translator); - } - } - - /** - * Sets the ValueTransformer. - * - * @param ValueTransformerInterface $valueTransformer - */ - public function setValueTransformer(ValueTransformerInterface $valueTransformer) - { - $this->injectLocaleAndTranslator($valueTransformer); - - $this->valueTransformer = $valueTransformer; - } - - /** - * Returns the ValueTransformer. - * - * @return ValueTransformerInterface - */ - public function getValueTransformer() - { - return $this->valueTransformer; - } - - /** - * Transforms the value if a value transformer is set. - * - * @param mixed $value The value to transform - * @return string - */ - protected function transform($value) - { - if ($value === null) - { - return ''; - } - else if (null === $this->valueTransformer) - { - return $value; - } - else - { - return $this->valueTransformer->transform($value); - } - } - - /** - * Reverse transforms a value if a value transformer is set. - * - * @param string $value The value to reverse transform - * @return mixed - */ - protected function reverseTransform($value) - { - if ($value === '') - { - return null; - } - else if (null === $this->valueTransformer) - { - return $value; - } - else - { - return $this->valueTransformer->reverseTransform($value); - } - } - - /** - * {@inheritDoc} - */ - public function updateFromObject(&$objectOrArray) - { - // TODO throw exception if not object or array - - if ($this->propertyPath !== null) - { - $this->propertyPath->rewind(); - $this->setData($this->readPropertyPath($objectOrArray, $this->propertyPath)); - } - else - { - // pass object through if the property path is empty - $this->setData($objectOrArray); - } - } - - /** - * {@inheritDoc} - */ - public function updateObject(&$objectOrArray) - { - // TODO throw exception if not object or array - - if ($this->propertyPath !== null) - { - $this->propertyPath->rewind(); - $this->updatePropertyPath($objectOrArray, $this->propertyPath); - } - } - - /** - * Recursively reads the value of the property path in the data - * - * @param array|object $objectOrArray An object or array - * @param PropertyPath $propertyPath A property path pointing to a property - * in the object/array. - */ - protected function readPropertyPath(&$objectOrArray, PropertyPath $propertyPath) - { - if (is_object($objectOrArray)) - { - $value = $this->readProperty($objectOrArray, $propertyPath); - } - // arrays need to be treated separately (due to PHP bug?) - // http://bugs.php.net/bug.php?id=52133 - else - { - if (!array_key_exists($propertyPath->getCurrent(), $objectOrArray)) - { - $objectOrArray[$propertyPath->getCurrent()] = array(); - } - - $value =& $objectOrArray[$propertyPath->getCurrent()]; + return $this->transformedData; } - if ($propertyPath->hasNext()) + /** + * {@inheritDoc} + */ + public function setPropertyPath($propertyPath) { - $propertyPath->next(); - - return $this->readPropertyPath($value, $propertyPath); + $this->propertyPath = empty($propertyPath) ? null : new PropertyPath($propertyPath); } - else - { - return $value; - } - } - protected function updatePropertyPath(&$objectOrArray, PropertyPath $propertyPath) - { - if ($propertyPath->hasNext()) + /** + * {@inheritDoc} + */ + public function getPropertyPath() { - if (is_object($objectOrArray)) - { - $value = $this->readProperty($objectOrArray, $propertyPath); - } - // arrays need to be treated separately (due to PHP bug?) - // http://bugs.php.net/bug.php?id=52133 - else - { - if (!array_key_exists($propertyPath->getCurrent(), $objectOrArray)) - { - $objectOrArray[$propertyPath->getCurrent()] = array(); + return $this->propertyPath; + } + + /** + * {@inheritDoc} + */ + public function setKey($key) + { + $this->key = (string)$key; + } + + /** + * {@inheritDoc} + */ + public function getKey() + { + return $this->key; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return is_null($this->parent) ? $this->key : $this->parent->getName().'['.$this->key.']'; + } + + /** + * {@inheritDoc} + */ + public function getId() + { + return is_null($this->parent) ? $this->key : $this->parent->getId().'_'.$this->key; + } + + /** + * {@inheritDoc} + */ + public function setRequired($required) + { + $this->required = $required; + } + + /** + * {@inheritDoc} + */ + public function isRequired() + { + if (is_null($this->parent) || $this->parent->isRequired()) { + return $this->required; + } else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public function isDisabled() + { + if (is_null($this->parent) || !$this->parent->isDisabled()) { + return $this->getOption('disabled'); + } else { + return true; + } + } + + /** + * {@inheritDoc} + */ + public function setGenerator(HtmlGeneratorInterface $generator) + { + $this->generator = $generator; + } + + /** + * {@inheritDoc} + */ + public function isMultipart() + { + return false; + } + + /** + * Returns true if the widget is hidden. + * + * @return Boolean true if the widget is hidden, false otherwise + */ + public function isHidden() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function setParent(FieldInterface $parent = null) + { + $this->parent = $parent; + } + + /** + * Returns the parent field. + * + * @return FieldInterface The parent field + */ + public function getParent() + { + return $this->parent; + } + + /** + * Updates the field with default data + * + * @see FieldInterface + */ + public function setData($data) + { + $this->data = $data; + $this->transformedData = $this->transform($data); + } + + /** + * Binds POST data to the field, transforms and validates it. + * + * @param string|array $taintedData The POST data + * @return boolean Whether the form is valid + * @throws AlreadyBoundException when the field is already bound + */ + public function bind($taintedData) + { + $this->transformedData = is_array($taintedData) || is_object($taintedData) ? $taintedData : (string)$taintedData; + $this->bound = true; + $this->errors = array(); + + if (is_string($this->transformedData) && $this->getOption('trim')) { + $this->transformedData = trim($this->transformedData); } - $value =& $objectOrArray[$propertyPath->getCurrent()]; - } - - $propertyPath->next(); - - $this->updatePropertyPath($value, $propertyPath); + try { + $this->data = $this->processData($data = $this->reverseTransform($this->transformedData)); + $this->transformedData = $this->transform($this->data); + } catch (TransformationFailedException $e) { + // TODO better text + // TESTME + $this->addError('invalid (localized)'); + } } - else + + /** + * Processes the bound reverse-transformed data. + * + * This method can be overridden if you want to modify the data entered + * by the user. Note that the data is already in reverse transformed format. + * + * This method will not be called if reverse transformation fails. + * + * @param mixed $data + * @return mixed + */ + protected function processData($data) { - $this->updateProperty($objectOrArray, $propertyPath); + return $data; } - } - /** - * Reads a specific element of the given data - * - * If the data is an array, the value at index $element is returned. - * - * If the data is an object, either the result of get{$element}(), - * is{$element}() or the property $element is returned. If none of these - * is publicly available, an exception is thrown - * - * @param object $object The data to read - * @param string $element The element to read from the data - * @return mixed The value of the element - */ - protected function readProperty($object, PropertyPath $propertyPath) - { - if ($propertyPath->isIndex()) + /** + * Returns the normalized data of the field. + * + * @return mixed When the field is not bound, the default data is returned. + * When the field is bound, the normalized bound data is + * returned if the field is valid, null otherwise. + */ + public function getData() { - if (!$object instanceof \ArrayAccess) - { - throw new InvalidPropertyException(sprintf('Index "%s" cannot be read from object of type "%s" because it doesn\'t implement \ArrayAccess', $propertyPath->getCurrent(), get_class($object))); - } - - return $object[$propertyPath->getCurrent()]; + return $this->data; } - else - { - $reflClass = new \ReflectionClass($object); - $getter = 'get'.ucfirst($propertyPath->getCurrent()); - $isser = 'is'.ucfirst($propertyPath->getCurrent()); - $property = $propertyPath->getCurrent(); - if ($reflClass->hasMethod($getter)) - { - if (!$reflClass->getMethod($getter)->isPublic()) - { - throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $getter, $reflClass->getName())); + /** + * Adds an error to the field. + * + * @see FieldInterface + */ + public function addError($message, PropertyPath $path = null, $type = null) + { + $this->errors[] = $message; + } + + /** + * Returns whether the field is bound. + * + * @return boolean true if the form is bound to input values, false otherwise + */ + public function isBound() + { + return $this->bound; + } + + /** + * Returns whether the field is valid. + * + * @return boolean + */ + public function isValid() + { + return $this->isBound() ? count($this->errors)==0 : false; // TESTME + } + + /** + * Returns weather there are errors. + * + * @return boolean true if form is bound and not valid + */ + public function hasErrors() + { + return $this->isBound() && !$this->isValid(); + } + + /** + * Returns all errors + * + * @return array An array of errors that occured during binding + */ + public function getErrors() + { + return $this->errors; + } + + /** + * Sets the locale of this field. + * + * @see Localizable + */ + public function setLocale($locale) + { + $this->locale = $locale; + + if ($this->valueTransformer !== null && $this->valueTransformer instanceof Localizable) { + $this->valueTransformer->setLocale($locale); + } + } + + /** + * Sets the translator of this field. + * + * @see Translatable + */ + public function setTranslator(TranslatorInterface $translator) + { + $this->translator = $translator; + + if ($this->valueTransformer !== null && $this->valueTransformer instanceof Translatable) { + $this->valueTransformer->setTranslator($translator); + } + } + + /** + * Translates the text using the associated translator, if available + * + * If no translator is available, the original text is returned without + * modification. + * + * @param string $text The text to translate + * @param array $parameters The parameters to insert in the text + * @return string The translated text + */ + protected function translate($text, array $parameters = array()) + { + if ($this->translator !== null) { + $text = $this->translator->translate($text, $parameters); } - return $object->$getter(); - } - else if ($reflClass->hasMethod($isser)) - { - if (!$reflClass->getMethod($isser)->isPublic()) - { - throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $isser, $reflClass->getName())); + return $text; + } + + /** + * Injects the locale and the translator into the given object, if set. + * + * The locale is injected only if the object implements Localizable. The + * translator is injected only if the object implements Translatable. + * + * @param object $object + */ + protected function injectLocaleAndTranslator($object) + { + if ($object instanceof Localizable) { + $object->setLocale($this->locale); } - return $object->$isser(); - } - else if ($reflClass->hasProperty($property)) - { - if (!$reflClass->getProperty($property)->isPublic()) - { - throw new PropertyAccessDeniedException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "get%s()" or "is%s()"?', $property, $reflClass->getName(), ucfirst($property), ucfirst($property))); + if (!is_null($this->translator) && $object instanceof Translatable) { + $object->setTranslator($this->translator); + } + } + + /** + * Sets the ValueTransformer. + * + * @param ValueTransformerInterface $valueTransformer + */ + public function setValueTransformer(ValueTransformerInterface $valueTransformer) + { + $this->injectLocaleAndTranslator($valueTransformer); + + $this->valueTransformer = $valueTransformer; + } + + /** + * Returns the ValueTransformer. + * + * @return ValueTransformerInterface + */ + public function getValueTransformer() + { + return $this->valueTransformer; + } + + /** + * Transforms the value if a value transformer is set. + * + * @param mixed $value The value to transform + * @return string + */ + protected function transform($value) + { + if ($value === null) { + return ''; + } else if (null === $this->valueTransformer) { + return $value; + } else { + return $this->valueTransformer->transform($value); + } + } + + /** + * Reverse transforms a value if a value transformer is set. + * + * @param string $value The value to reverse transform + * @return mixed + */ + protected function reverseTransform($value) + { + if ($value === '') { + return null; + } else if (null === $this->valueTransformer) { + return $value; + } else { + return $this->valueTransformer->reverseTransform($value); + } + } + + /** + * {@inheritDoc} + */ + public function updateFromObject(&$objectOrArray) + { + // TODO throw exception if not object or array + + if ($this->propertyPath !== null) { + $this->propertyPath->rewind(); + $this->setData($this->readPropertyPath($objectOrArray, $this->propertyPath)); + } else { + // pass object through if the property path is empty + $this->setData($objectOrArray); + } + } + + /** + * {@inheritDoc} + */ + public function updateObject(&$objectOrArray) + { + // TODO throw exception if not object or array + + if ($this->propertyPath !== null) { + $this->propertyPath->rewind(); + $this->updatePropertyPath($objectOrArray, $this->propertyPath); + } + } + + /** + * Recursively reads the value of the property path in the data + * + * @param array|object $objectOrArray An object or array + * @param PropertyPath $propertyPath A property path pointing to a property + * in the object/array. + */ + protected function readPropertyPath(&$objectOrArray, PropertyPath $propertyPath) + { + if (is_object($objectOrArray)) { + $value = $this->readProperty($objectOrArray, $propertyPath); + } + // arrays need to be treated separately (due to PHP bug?) + // http://bugs.php.net/bug.php?id=52133 + else { + if (!array_key_exists($propertyPath->getCurrent(), $objectOrArray)) { + $objectOrArray[$propertyPath->getCurrent()] = array(); + } + + $value =& $objectOrArray[$propertyPath->getCurrent()]; } - return $object->$property; - } - else - { - throw new InvalidPropertyException(sprintf('Neither property "%s" nor method "%s()" nor method "%s()" exists in class "%s"', $property, $getter, $isser, $reflClass->getName())); - } + if ($propertyPath->hasNext()) { + $propertyPath->next(); + + return $this->readPropertyPath($value, $propertyPath); + } else { + return $value; + } } - } - protected function updateProperty(&$objectOrArray, PropertyPath $propertyPath) - { - if (is_object($objectOrArray) && $propertyPath->isIndex()) + protected function updatePropertyPath(&$objectOrArray, PropertyPath $propertyPath) { - if (!$objectOrArray instanceof \ArrayAccess) - { - throw new InvalidPropertyException(sprintf('Index "%s" cannot be modified in object of type "%s" because it doesn\'t implement \ArrayAccess', $propertyPath->getCurrent(), get_class($objectOrArray))); - } + if ($propertyPath->hasNext()) { + if (is_object($objectOrArray)) { + $value = $this->readProperty($objectOrArray, $propertyPath); + } + // arrays need to be treated separately (due to PHP bug?) + // http://bugs.php.net/bug.php?id=52133 + else { + if (!array_key_exists($propertyPath->getCurrent(), $objectOrArray)) { + $objectOrArray[$propertyPath->getCurrent()] = array(); + } - $objectOrArray[$propertyPath->getCurrent()] = $this->getData(); + $value =& $objectOrArray[$propertyPath->getCurrent()]; + } + + $propertyPath->next(); + + $this->updatePropertyPath($value, $propertyPath); + } else { + $this->updateProperty($objectOrArray, $propertyPath); + } } - else if (is_object($objectOrArray)) - { - $reflClass = new \ReflectionClass($objectOrArray); - $setter = 'set'.ucfirst($propertyPath->getCurrent()); - $property = $propertyPath->getCurrent(); - if ($reflClass->hasMethod($setter)) - { - if (!$reflClass->getMethod($setter)->isPublic()) - { - throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $setter, $reflClass->getName())); + /** + * Reads a specific element of the given data + * + * If the data is an array, the value at index $element is returned. + * + * If the data is an object, either the result of get{$element}(), + * is{$element}() or the property $element is returned. If none of these + * is publicly available, an exception is thrown + * + * @param object $object The data to read + * @param string $element The element to read from the data + * @return mixed The value of the element + */ + protected function readProperty($object, PropertyPath $propertyPath) + { + if ($propertyPath->isIndex()) { + if (!$object instanceof \ArrayAccess) { + throw new InvalidPropertyException(sprintf('Index "%s" cannot be read from object of type "%s" because it doesn\'t implement \ArrayAccess', $propertyPath->getCurrent(), get_class($object))); + } + + return $object[$propertyPath->getCurrent()]; + } else { + $reflClass = new \ReflectionClass($object); + $getter = 'get'.ucfirst($propertyPath->getCurrent()); + $isser = 'is'.ucfirst($propertyPath->getCurrent()); + $property = $propertyPath->getCurrent(); + + if ($reflClass->hasMethod($getter)) { + if (!$reflClass->getMethod($getter)->isPublic()) { + throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $getter, $reflClass->getName())); + } + + return $object->$getter(); + } else if ($reflClass->hasMethod($isser)) { + if (!$reflClass->getMethod($isser)->isPublic()) { + throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $isser, $reflClass->getName())); + } + + return $object->$isser(); + } else if ($reflClass->hasProperty($property)) { + if (!$reflClass->getProperty($property)->isPublic()) { + throw new PropertyAccessDeniedException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "get%s()" or "is%s()"?', $property, $reflClass->getName(), ucfirst($property), ucfirst($property))); + } + + return $object->$property; + } else { + throw new InvalidPropertyException(sprintf('Neither property "%s" nor method "%s()" nor method "%s()" exists in class "%s"', $property, $getter, $isser, $reflClass->getName())); + } + } + } + + protected function updateProperty(&$objectOrArray, PropertyPath $propertyPath) + { + if (is_object($objectOrArray) && $propertyPath->isIndex()) { + if (!$objectOrArray instanceof \ArrayAccess) { + throw new InvalidPropertyException(sprintf('Index "%s" cannot be modified in object of type "%s" because it doesn\'t implement \ArrayAccess', $propertyPath->getCurrent(), get_class($objectOrArray))); + } + + $objectOrArray[$propertyPath->getCurrent()] = $this->getData(); + } else if (is_object($objectOrArray)) { + $reflClass = new \ReflectionClass($objectOrArray); + $setter = 'set'.ucfirst($propertyPath->getCurrent()); + $property = $propertyPath->getCurrent(); + + if ($reflClass->hasMethod($setter)) { + if (!$reflClass->getMethod($setter)->isPublic()) { + throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $setter, $reflClass->getName())); + } + + $objectOrArray->$setter($this->getData()); + } else if ($reflClass->hasProperty($property)) { + if (!$reflClass->getProperty($property)->isPublic()) { + throw new PropertyAccessDeniedException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "set%s()"?', $property, $reflClass->getName(), ucfirst($property))); + } + + $objectOrArray->$property = $this->getData(); + } else { + throw new InvalidPropertyException(sprintf('Neither element "%s" nor method "%s()" exists in class "%s"', $property, $setter, $reflClass->getName())); + } + } else { + $objectOrArray[$propertyPath->getCurrent()] = $this->getData(); + } + } + + /** + * {@inheritDoc} + */ + public function renderErrors() + { + $html = ''; + + if ($this->hasErrors()) { + $html .= "
- * class Entity
- * {
- * public $location;
- * }
- *
- * class Location
- * {
- * public $longitude;
- * public $latitude;
- * }
- *
- * $entity = new Entity();
- * $entity->location = new Location();
- *
- * $form = new Form('entity', $entity, $validator);
- *
- * $locationGroup = new FieldGroup('location');
- * $locationGroup->add(new TextField('longitude'));
- * $locationGroup->add(new TextField('latitude'));
- *
- * $form->add($locationGroup);
- *
- *
- * @param FieldInterface $field
- */
- public function add(FieldInterface $field)
- {
- if ($this->isBound())
- {
- throw new AlreadyBoundException('You cannot add fields after binding a form');
+ parent::__construct($key, $options);
}
- $this->fields[$field->getKey()] = $field;
-
- $field->setParent($this);
- $field->setLocale($this->locale);
- $field->setGenerator($this->generator);
-
- if ($this->translator !== null)
+ /**
+ * Clones this group
+ */
+ public function __clone()
{
- $field->setTranslator($this->translator);
- }
-
- $data = $this->getTransformedData();
-
- // if the property "data" is NULL, getTransformedData() returns an empty
- // string
- if (!empty($data) && $field->getPropertyPath() !== null)
- {
- $field->updateFromObject($data);
- }
-
- return $field;
- }
-
- /**
- * Merges a field group into this group. The group must have a unique name
- * within the group. Otherwise the existing field is overwritten.
- *
- * Contrary to added groups, merged groups operate on the same object as
- * the group they are merged into.
- *
- *
- * class Entity
- * {
- * public $longitude;
- * public $latitude;
- * }
- *
- * $entity = new Entity();
- *
- * $form = new Form('entity', $entity, $validator);
- *
- * $locationGroup = new FieldGroup('location');
- * $locationGroup->add(new TextField('longitude'));
- * $locationGroup->add(new TextField('latitude'));
- *
- * $form->merge($locationGroup);
- *
- *
- * @param FieldGroup $group
- */
- public function merge(FieldGroup $group)
- {
- if ($group->isBound())
- {
- throw new AlreadyBoundException('A bound form group cannot be merged');
- }
-
- foreach ($group as $field)
- {
- $group->remove($field->getKey());
- $this->add($field);
-
- if (($path = $group->getPropertyPath()) !== null)
- {
- $field->setPropertyPath($path.'.'.$field->getPropertyPath());
- }
- }
-
- return $this;
- }
-
- /**
- * Removes the field with the given key.
- *
- * @param string $key
- */
- public function remove($key)
- {
- $this->fields[$key]->setParent(null);
-
- unset($this->fields[$key]);
- }
-
- /**
- * Returns whether a field with the given key exists.
- *
- * @param string $key
- * @return boolean
- */
- public function has($key)
- {
- return isset($this->fields[$key]);
- }
-
- /**
- * Returns the field with the given key.
- *
- * @param string $key
- * @return FieldInterface
- */
- public function get($key)
- {
- if (isset($this->fields[$key]))
- {
- return $this->fields[$key];
- }
-
- throw new \InvalidArgumentException(sprintf('Field "%s" does not exist.', $key));
- }
-
- /**
- * Returns all fields in this group
- *
- * @return array
- */
- public function getFields()
- {
- return $this->fields;
- }
-
- /**
- * Returns an array of hidden fields from the current schema.
- *
- * @param boolean $recursive Whether to recur through embedded schemas
- *
- * @return array
- */
- public function getHiddenFields($recursive = true)
- {
- $fields = array();
-
- foreach ($this->fields as $field)
- {
- if ($field instanceof FieldGroup)
- {
- if ($recursive)
- {
- $fields = array_merge($fields, $field->getHiddenFields($recursive));
+ foreach ($this->fields as $name => $field) {
+ $this->fields[$name] = clone $field;
}
- }
- else if ($field->isHidden())
- {
- $fields[] = $field;
- }
}
- return $fields;
- }
-
- /**
- * Initializes the field group with an object to operate on
- *
- * @see FieldInterface
- */
- public function setData($data)
- {
- parent::setData($data);
-
- // get transformed data and pass its values to child fields
- $data = $this->getTransformedData();
-
- if (!empty($data) && !is_array($data) && !is_object($data))
+ /**
+ * Adds a new field to this group. A field must have a unique name within
+ * the group. Otherwise the existing field is overwritten.
+ *
+ * If you add a nested group, this group should also be represented in the
+ * object hierarchy. If you want to add a group that operates on the same
+ * hierarchy level, use merge().
+ *
+ *
+ * class Entity
+ * {
+ * public $location;
+ * }
+ *
+ * class Location
+ * {
+ * public $longitude;
+ * public $latitude;
+ * }
+ *
+ * $entity = new Entity();
+ * $entity->location = new Location();
+ *
+ * $form = new Form('entity', $entity, $validator);
+ *
+ * $locationGroup = new FieldGroup('location');
+ * $locationGroup->add(new TextField('longitude'));
+ * $locationGroup->add(new TextField('latitude'));
+ *
+ * $form->add($locationGroup);
+ *
+ *
+ * @param FieldInterface $field
+ */
+ public function add(FieldInterface $field)
{
- throw new \InvalidArgumentException(sprintf('Expected argument of type object or array, %s given', gettype($data)));
- }
-
- if (!empty($data))
- {
- $iterator = new RecursiveFieldsWithPropertyPathIterator($this);
- $iterator = new \RecursiveIteratorIterator($iterator);
-
- foreach ($iterator as $field)
- {
- $field->updateFromObject($data);
- }
- }
- }
-
- /**
- * Returns the data of the field as it is displayed to the user.
- *
- * @see FieldInterface
- */
- public function getDisplayedData()
- {
- $values = array();
-
- foreach ($this->fields as $key => $field)
- {
- $values[$key] = $field->getDisplayedData();
- }
-
- return $values;
- }
-
- /**
- * Binds POST data to the field, transforms and validates it.
- *
- * @param string|array $taintedData The POST data
- * @return boolean Whether the form is valid
- */
- public function bind($taintedData)
- {
- if ($taintedData === null)
- {
- $taintedData = array();
- }
-
- if (!is_array($taintedData))
- {
- throw new UnexpectedTypeException('You must pass an array parameter to the bind() method');
- }
-
- foreach ($this->fields as $key => $field)
- {
- if (!isset($taintedData[$key]))
- {
- $taintedData[$key] = null;
- }
- }
-
- foreach ($taintedData as $key => $value)
- {
- if ($this->has($key))
- {
- $this->fields[$key]->bind($value);
- }
- }
-
- $data = $this->getTransformedData();
- $iterator = new RecursiveFieldsWithPropertyPathIterator($this);
- $iterator = new \RecursiveIteratorIterator($iterator);
-
- foreach ($iterator as $field)
- {
- $field->updateObject($data);
- }
-
- // bind and reverse transform the data
- parent::bind($data);
-
- $this->extraFields = array();
-
- foreach ($taintedData as $key => $value)
- {
- if (!$this->has($key))
- {
- $this->extraFields[] = $key;
- }
- }
- }
-
- /**
- * Returns whether this form was bound with extra fields
- *
- * @return boolean
- */
- public function isBoundWithExtraFields()
- {
- // TODO: integrate the field names in the error message
- return count($this->extraFields) > 0;
- }
-
- /**
- * Returns whether the field is valid.
- *
- * @return boolean
- */
- public function isValid()
- {
- if (!parent::isValid())
- {
- return false;
- }
-
- foreach ($this->fields as $field)
- {
- if (!$field->isValid())
- {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * {@inheritDoc}
- */
- public function addError($message, PropertyPath $path = null, $type = null)
- {
- if ($path !== null)
- {
- if ($type === self::FIELD_ERROR && $path->hasNext())
- {
- $path->next();
-
- if ($this->has($path->getCurrent()) && !$this->get($path->getCurrent())->isHidden())
- {
- $this->get($path->getCurrent())->addError($message, $path, $type);
-
- return;
+ if ($this->isBound()) {
+ throw new AlreadyBoundException('You cannot add fields after binding a form');
}
- }
- else if ($type === self::DATA_ERROR)
- {
+
+ $this->fields[$field->getKey()] = $field;
+
+ $field->setParent($this);
+ $field->setLocale($this->locale);
+ $field->setGenerator($this->generator);
+
+ if ($this->translator !== null) {
+ $field->setTranslator($this->translator);
+ }
+
+ $data = $this->getTransformedData();
+
+ // if the property "data" is NULL, getTransformedData() returns an empty
+ // string
+ if (!empty($data) && $field->getPropertyPath() !== null) {
+ $field->updateFromObject($data);
+ }
+
+ return $field;
+ }
+
+ /**
+ * Merges a field group into this group. The group must have a unique name
+ * within the group. Otherwise the existing field is overwritten.
+ *
+ * Contrary to added groups, merged groups operate on the same object as
+ * the group they are merged into.
+ *
+ *
+ * class Entity
+ * {
+ * public $longitude;
+ * public $latitude;
+ * }
+ *
+ * $entity = new Entity();
+ *
+ * $form = new Form('entity', $entity, $validator);
+ *
+ * $locationGroup = new FieldGroup('location');
+ * $locationGroup->add(new TextField('longitude'));
+ * $locationGroup->add(new TextField('latitude'));
+ *
+ * $form->merge($locationGroup);
+ *
+ *
+ * @param FieldGroup $group
+ */
+ public function merge(FieldGroup $group)
+ {
+ if ($group->isBound()) {
+ throw new AlreadyBoundException('A bound form group cannot be merged');
+ }
+
+ foreach ($group as $field) {
+ $group->remove($field->getKey());
+ $this->add($field);
+
+ if (($path = $group->getPropertyPath()) !== null) {
+ $field->setPropertyPath($path.'.'.$field->getPropertyPath());
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Removes the field with the given key.
+ *
+ * @param string $key
+ */
+ public function remove($key)
+ {
+ $this->fields[$key]->setParent(null);
+
+ unset($this->fields[$key]);
+ }
+
+ /**
+ * Returns whether a field with the given key exists.
+ *
+ * @param string $key
+ * @return boolean
+ */
+ public function has($key)
+ {
+ return isset($this->fields[$key]);
+ }
+
+ /**
+ * Returns the field with the given key.
+ *
+ * @param string $key
+ * @return FieldInterface
+ */
+ public function get($key)
+ {
+ if (isset($this->fields[$key])) {
+ return $this->fields[$key];
+ }
+
+ throw new \InvalidArgumentException(sprintf('Field "%s" does not exist.', $key));
+ }
+
+ /**
+ * Returns all fields in this group
+ *
+ * @return array
+ */
+ public function getFields()
+ {
+ return $this->fields;
+ }
+
+ /**
+ * Returns an array of hidden fields from the current schema.
+ *
+ * @param boolean $recursive Whether to recur through embedded schemas
+ *
+ * @return array
+ */
+ public function getHiddenFields($recursive = true)
+ {
+ $fields = array();
+
+ foreach ($this->fields as $field) {
+ if ($field instanceof FieldGroup) {
+ if ($recursive) {
+ $fields = array_merge($fields, $field->getHiddenFields($recursive));
+ }
+ } else if ($field->isHidden()) {
+ $fields[] = $field;
+ }
+ }
+
+ return $fields;
+ }
+
+ /**
+ * Initializes the field group with an object to operate on
+ *
+ * @see FieldInterface
+ */
+ public function setData($data)
+ {
+ parent::setData($data);
+
+ // get transformed data and pass its values to child fields
+ $data = $this->getTransformedData();
+
+ if (!empty($data) && !is_array($data) && !is_object($data)) {
+ throw new \InvalidArgumentException(sprintf('Expected argument of type object or array, %s given', gettype($data)));
+ }
+
+ if (!empty($data)) {
+ $iterator = new RecursiveFieldsWithPropertyPathIterator($this);
+ $iterator = new \RecursiveIteratorIterator($iterator);
+
+ foreach ($iterator as $field) {
+ $field->updateFromObject($data);
+ }
+ }
+ }
+
+ /**
+ * Returns the data of the field as it is displayed to the user.
+ *
+ * @see FieldInterface
+ */
+ public function getDisplayedData()
+ {
+ $values = array();
+
+ foreach ($this->fields as $key => $field) {
+ $values[$key] = $field->getDisplayedData();
+ }
+
+ return $values;
+ }
+
+ /**
+ * Binds POST data to the field, transforms and validates it.
+ *
+ * @param string|array $taintedData The POST data
+ * @return boolean Whether the form is valid
+ */
+ public function bind($taintedData)
+ {
+ if ($taintedData === null) {
+ $taintedData = array();
+ }
+
+ if (!is_array($taintedData)) {
+ throw new UnexpectedTypeException('You must pass an array parameter to the bind() method');
+ }
+
+ foreach ($this->fields as $key => $field) {
+ if (!isset($taintedData[$key])) {
+ $taintedData[$key] = null;
+ }
+ }
+
+ foreach ($taintedData as $key => $value) {
+ if ($this->has($key)) {
+ $this->fields[$key]->bind($value);
+ }
+ }
+
+ $data = $this->getTransformedData();
$iterator = new RecursiveFieldsWithPropertyPathIterator($this);
$iterator = new \RecursiveIteratorIterator($iterator);
- foreach ($iterator as $field)
- {
- if (null !== ($fieldPath = $field->getPropertyPath()))
- {
- $fieldPath->rewind();
-
- if ($fieldPath->getCurrent() === $path->getCurrent() && !$field->isHidden())
- {
- if ($path->hasNext())
- {
- $path->next();
- }
-
- $field->addError($message, $path, $type);
-
- return;
- }
- }
+ foreach ($iterator as $field) {
+ $field->updateObject($data);
+ }
+
+ // bind and reverse transform the data
+ parent::bind($data);
+
+ $this->extraFields = array();
+
+ foreach ($taintedData as $key => $value) {
+ if (!$this->has($key)) {
+ $this->extraFields[] = $key;
+ }
}
- }
}
- parent::addError($message);
- }
-
- /**
- * Returns whether the field requires a multipart form.
- *
- * @return boolean
- */
- public function isMultipart()
- {
- foreach ($this->fields as $field)
+ /**
+ * Returns whether this form was bound with extra fields
+ *
+ * @return boolean
+ */
+ public function isBoundWithExtraFields()
{
- if ($field->isMultipart())
- {
+ // TODO: integrate the field names in the error message
+ return count($this->extraFields) > 0;
+ }
+
+ /**
+ * Returns whether the field is valid.
+ *
+ * @return boolean
+ */
+ public function isValid()
+ {
+ if (!parent::isValid()) {
+ return false;
+ }
+
+ foreach ($this->fields as $field) {
+ if (!$field->isValid()) {
+ return false;
+ }
+ }
+
return true;
- }
}
- return false;
- }
-
- /**
- * Sets the renderer.
- *
- * @param RendererInterface $renderer
- */
- public function setRenderer(RendererInterface $renderer)
- {
- $this->renderer = $renderer;
- }
-
- /**
- * Returns the current renderer.
- *
- * @return RendererInterface
- */
- public function getRenderer()
- {
- return $this->renderer;
- }
-
- /**
- * Delegates the rendering of the field to the renderer set.
- *
- * @return string The rendered widget
- */
- public function render(array $attributes = array())
- {
- $this->injectLocaleAndTranslator($this->renderer);
-
- return $this->renderer->render($this, $attributes);
- }
-
- /**
- * Delegates the rendering of the field to the renderer set.
- *
- * @return string The rendered widget
- */
- public function renderErrors()
- {
- $this->injectLocaleAndTranslator($this->renderer);
-
- return $this->renderer->renderErrors($this);
- }
- /**
- * Renders hidden form fields.
- *
- * @param boolean $recursive False will prevent hidden fields from embedded forms from rendering
- *
- * @return string
- */
- public function renderHiddenFields($recursive = true)
- {
- $output = '';
-
- foreach ($this->getHiddenFields($recursive) as $field)
+ /**
+ * {@inheritDoc}
+ */
+ public function addError($message, PropertyPath $path = null, $type = null)
{
- $output .= $field->render();
+ if ($path !== null) {
+ if ($type === self::FIELD_ERROR && $path->hasNext()) {
+ $path->next();
+
+ if ($this->has($path->getCurrent()) && !$this->get($path->getCurrent())->isHidden()) {
+ $this->get($path->getCurrent())->addError($message, $path, $type);
+
+ return;
+ }
+ } else if ($type === self::DATA_ERROR) {
+ $iterator = new RecursiveFieldsWithPropertyPathIterator($this);
+ $iterator = new \RecursiveIteratorIterator($iterator);
+
+ foreach ($iterator as $field) {
+ if (null !== ($fieldPath = $field->getPropertyPath())) {
+ $fieldPath->rewind();
+
+ if ($fieldPath->getCurrent() === $path->getCurrent() && !$field->isHidden()) {
+ if ($path->hasNext()) {
+ $path->next();
+ }
+
+ $field->addError($message, $path, $type);
+
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ parent::addError($message);
}
- return $output;
- }
-
- /**
- * Returns true if the bound field exists (implements the \ArrayAccess interface).
- *
- * @param string $key The key of the bound field
- *
- * @return Boolean true if the widget exists, false otherwise
- */
- public function offsetExists($key)
- {
- return $this->has($key);
- }
-
- /**
- * Returns the form field associated with the name (implements the \ArrayAccess interface).
- *
- * @param string $key The offset of the value to get
- *
- * @return Field A form field instance
- */
- public function offsetGet($key)
- {
- return $this->get($key);
- }
-
- /**
- * Throws an exception saying that values cannot be set (implements the \ArrayAccess interface).
- *
- * @param string $offset (ignored)
- * @param string $value (ignored)
- *
- * @throws \LogicException
- */
- public function offsetSet($key, $field)
- {
- throw new \LogicException('Use the method add() to add fields');
- }
-
- /**
- * Throws an exception saying that values cannot be unset (implements the \ArrayAccess interface).
- *
- * @param string $key
- *
- * @throws \LogicException
- */
- public function offsetUnset($key)
- {
- return $this->remove($key);
- }
-
- /**
- * Returns the iterator for this group.
- *
- * @return \ArrayIterator
- */
- public function getIterator()
- {
- return new \ArrayIterator($this->fields);
- }
-
- /**
- * Returns the number of form fields (implements the \Countable interface).
- *
- * @return integer The number of embedded form fields
- */
- public function count()
- {
- return count($this->fields);
- }
-
- /**
- * Sets the locale of this field.
- *
- * @see Localizable
- */
- public function setLocale($locale)
- {
- parent::setLocale($locale);
-
- foreach ($this->fields as $field)
+ /**
+ * Returns whether the field requires a multipart form.
+ *
+ * @return boolean
+ */
+ public function isMultipart()
{
- $field->setLocale($locale);
+ foreach ($this->fields as $field) {
+ if ($field->isMultipart()) {
+ return true;
+ }
+ }
+
+ return false;
}
- }
- /**
- * Sets the translator of this field.
- *
- * @see Translatable
- */
- public function setTranslator(TranslatorInterface $translator)
- {
- parent::setTranslator($translator);
-
- foreach ($this->fields as $field)
+ /**
+ * Sets the renderer.
+ *
+ * @param RendererInterface $renderer
+ */
+ public function setRenderer(RendererInterface $renderer)
{
- $field->setTranslator($translator);
+ $this->renderer = $renderer;
}
- }
- /**
- * Distributes the generator among all nested fields
- *
- * @param HtmlGeneratorInterface $generator
- */
- public function setGenerator(HtmlGeneratorInterface $generator)
- {
- parent::setGenerator($generator);
-
- // TESTME
- foreach ($this->fields as $field)
+ /**
+ * Returns the current renderer.
+ *
+ * @return RendererInterface
+ */
+ public function getRenderer()
{
- $field->setGenerator($generator);
+ return $this->renderer;
+ }
+
+ /**
+ * Delegates the rendering of the field to the renderer set.
+ *
+ * @return string The rendered widget
+ */
+ public function render(array $attributes = array())
+ {
+ $this->injectLocaleAndTranslator($this->renderer);
+
+ return $this->renderer->render($this, $attributes);
+ }
+
+ /**
+ * Delegates the rendering of the field to the renderer set.
+ *
+ * @return string The rendered widget
+ */
+ public function renderErrors()
+ {
+ $this->injectLocaleAndTranslator($this->renderer);
+
+ return $this->renderer->renderErrors($this);
+ }
+ /**
+ * Renders hidden form fields.
+ *
+ * @param boolean $recursive False will prevent hidden fields from embedded forms from rendering
+ *
+ * @return string
+ */
+ public function renderHiddenFields($recursive = true)
+ {
+ $output = '';
+
+ foreach ($this->getHiddenFields($recursive) as $field) {
+ $output .= $field->render();
+ }
+
+ return $output;
+ }
+
+ /**
+ * Returns true if the bound field exists (implements the \ArrayAccess interface).
+ *
+ * @param string $key The key of the bound field
+ *
+ * @return Boolean true if the widget exists, false otherwise
+ */
+ public function offsetExists($key)
+ {
+ return $this->has($key);
+ }
+
+ /**
+ * Returns the form field associated with the name (implements the \ArrayAccess interface).
+ *
+ * @param string $key The offset of the value to get
+ *
+ * @return Field A form field instance
+ */
+ public function offsetGet($key)
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * Throws an exception saying that values cannot be set (implements the \ArrayAccess interface).
+ *
+ * @param string $offset (ignored)
+ * @param string $value (ignored)
+ *
+ * @throws \LogicException
+ */
+ public function offsetSet($key, $field)
+ {
+ throw new \LogicException('Use the method add() to add fields');
+ }
+
+ /**
+ * Throws an exception saying that values cannot be unset (implements the \ArrayAccess interface).
+ *
+ * @param string $key
+ *
+ * @throws \LogicException
+ */
+ public function offsetUnset($key)
+ {
+ return $this->remove($key);
+ }
+
+ /**
+ * Returns the iterator for this group.
+ *
+ * @return \ArrayIterator
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->fields);
+ }
+
+ /**
+ * Returns the number of form fields (implements the \Countable interface).
+ *
+ * @return integer The number of embedded form fields
+ */
+ public function count()
+ {
+ return count($this->fields);
+ }
+
+ /**
+ * Sets the locale of this field.
+ *
+ * @see Localizable
+ */
+ public function setLocale($locale)
+ {
+ parent::setLocale($locale);
+
+ foreach ($this->fields as $field) {
+ $field->setLocale($locale);
+ }
+ }
+
+ /**
+ * Sets the translator of this field.
+ *
+ * @see Translatable
+ */
+ public function setTranslator(TranslatorInterface $translator)
+ {
+ parent::setTranslator($translator);
+
+ foreach ($this->fields as $field) {
+ $field->setTranslator($translator);
+ }
+ }
+
+ /**
+ * Distributes the generator among all nested fields
+ *
+ * @param HtmlGeneratorInterface $generator
+ */
+ public function setGenerator(HtmlGeneratorInterface $generator)
+ {
+ parent::setGenerator($generator);
+
+ // TESTME
+ foreach ($this->fields as $field) {
+ $field->setGenerator($generator);
+ }
}
- }
}
diff --git a/src/Symfony/Components/Form/FieldInterface.php b/src/Symfony/Components/Form/FieldInterface.php
index 1b4381ac85..e213c583eb 100644
--- a/src/Symfony/Components/Form/FieldInterface.php
+++ b/src/Symfony/Components/Form/FieldInterface.php
@@ -12,248 +12,248 @@ use Symfony\Components\I18N\TranslatorInterface;
*/
interface FieldInterface extends Localizable, Translatable
{
- /**
- * Marks a constraint violation in a form field
- * @var integer
- */
- const FIELD_ERROR = 0;
+ /**
+ * Marks a constraint violation in a form field
+ * @var integer
+ */
+ const FIELD_ERROR = 0;
- /**
- * Marks a constraint violation in the data of a form field
- * @var integer
- */
- const DATA_ERROR = 1;
+ /**
+ * Marks a constraint violation in the data of a form field
+ * @var integer
+ */
+ const DATA_ERROR = 1;
- /**
- * Clones this field.
- */
- public function __clone();
+ /**
+ * Clones this field.
+ */
+ public function __clone();
- /**
- * Sets the parent field.
- *
- * @param FieldInterface $parent The parent field
- */
- public function setParent(FieldInterface $parent = null);
+ /**
+ * Sets the parent field.
+ *
+ * @param FieldInterface $parent The parent field
+ */
+ public function setParent(FieldInterface $parent = null);
- /**
- * Sets the key by which the field is identified in field groups.
- *
- * Once this field is nested in a field group, i.e. after setParent() was
- * called for the first time, this method should throw an exception.
- *
- * @param string $key The key of the field
- * @throws BadMethodCallException When the field already has a parent
- */
- public function setKey($key);
+ /**
+ * Sets the key by which the field is identified in field groups.
+ *
+ * Once this field is nested in a field group, i.e. after setParent() was
+ * called for the first time, this method should throw an exception.
+ *
+ * @param string $key The key of the field
+ * @throws BadMethodCallException When the field already has a parent
+ */
+ public function setKey($key);
- /**
- * Returns the key by which the field is identified in field groups.
- *
- * @return string The key of the field.
- */
- public function getKey();
+ /**
+ * Returns the key by which the field is identified in field groups.
+ *
+ * @return string The key of the field.
+ */
+ public function getKey();
- /**
- * Returns the name of the field.
- *
- * @return string When the field has no parent, the name is equal to its
- * key. If the field has a parent, the name is composed of
- * the parent's name and the field's key, where the field's
- * key is wrapped in squared brackets
- * (e.g. "parent_name[field_key]")
- */
- public function getName();
+ /**
+ * Returns the name of the field.
+ *
+ * @return string When the field has no parent, the name is equal to its
+ * key. If the field has a parent, the name is composed of
+ * the parent's name and the field's key, where the field's
+ * key is wrapped in squared brackets
+ * (e.g. "parent_name[field_key]")
+ */
+ public function getName();
- /**
- * Returns the ID of the field.
- *
- * @return string The ID of a field is equal to its name, where all
- * sequences of squared brackets are replaced by a single
- * underscore (e.g. if the name is "parent_name[field_key]",
- * the ID is "parent_name_field_key").
- */
- public function getId();
+ /**
+ * Returns the ID of the field.
+ *
+ * @return string The ID of a field is equal to its name, where all
+ * sequences of squared brackets are replaced by a single
+ * underscore (e.g. if the name is "parent_name[field_key]",
+ * the ID is "parent_name_field_key").
+ */
+ public function getId();
- /**
- * Sets the property path
- *
- * The property path determines the property or a sequence of properties
- * that a field updates in the data of the field group.
- *
- * @param string $propertyPath
- */
- public function setPropertyPath($propertyPath);
+ /**
+ * Sets the property path
+ *
+ * The property path determines the property or a sequence of properties
+ * that a field updates in the data of the field group.
+ *
+ * @param string $propertyPath
+ */
+ public function setPropertyPath($propertyPath);
- /**
- * Returns the property path of the field
- *
- * @return PropertyPath
- */
- public function getPropertyPath();
+ /**
+ * Returns the property path of the field
+ *
+ * @return PropertyPath
+ */
+ public function getPropertyPath();
- /**
- * Writes a property value of the object into the field
- *
- * The chosen property is determined by the field's property path.
- *
- * @param array|object $objectOrArray
- */
- public function updateFromObject(&$objectOrArray);
+ /**
+ * Writes a property value of the object into the field
+ *
+ * The chosen property is determined by the field's property path.
+ *
+ * @param array|object $objectOrArray
+ */
+ public function updateFromObject(&$objectOrArray);
- /**
- * Writes a the field value into a property of the object
- *
- * The chosen property is determined by the field's property path.
- *
- * @param array|object $objectOrArray
- */
- public function updateObject(&$objectOrArray);
+ /**
+ * Writes a the field value into a property of the object
+ *
+ * The chosen property is determined by the field's property path.
+ *
+ * @param array|object $objectOrArray
+ */
+ public function updateObject(&$objectOrArray);
- /**
- * Returns the normalized data of the field.
- *
- * @return mixed When the field is not bound, the default data is returned.
- * When the field is bound, the normalized bound data is
- * returned if the field is valid, null otherwise.
- */
- public function getData();
+ /**
+ * Returns the normalized data of the field.
+ *
+ * @return mixed When the field is not bound, the default data is returned.
+ * When the field is bound, the normalized bound data is
+ * returned if the field is valid, null otherwise.
+ */
+ public function getData();
- /**
- * Returns the data of the field as it is displayed to the user.
- *
- * @return string|array When the field is not bound, the transformed
- * default data is returned. When the field is bound,
- * the bound data is returned.
- */
- public function getDisplayedData();
+ /**
+ * Returns the data of the field as it is displayed to the user.
+ *
+ * @return string|array When the field is not bound, the transformed
+ * default data is returned. When the field is bound,
+ * the bound data is returned.
+ */
+ public function getDisplayedData();
- /**
- * Sets the default data
- *
- * @param mixed $default The default data
- * @throws UnexpectedTypeException If the default data is invalid
- */
- public function setData($default);
+ /**
+ * Sets the default data
+ *
+ * @param mixed $default The default data
+ * @throws UnexpectedTypeException If the default data is invalid
+ */
+ public function setData($default);
- /**
- * Binds POST data to the field, transforms and validates it.
- *
- * @param string|array $taintedData The POST data
- * @return boolean Whether the form is valid
- * @throws InvalidConfigurationException when the field is not configured
- * correctly
- */
- public function bind($taintedData);
+ /**
+ * Binds POST data to the field, transforms and validates it.
+ *
+ * @param string|array $taintedData The POST data
+ * @return boolean Whether the form is valid
+ * @throws InvalidConfigurationException when the field is not configured
+ * correctly
+ */
+ public function bind($taintedData);
- /**
- * Recursively adds constraint violations to the fields
- *
- * Violations in the form fields usually have property paths like:
- *
- *
- * iterator[firstName].data
- * iterator[firstName].displayedData
- * iterator[Address].iterator[street].displayedData
- * ...
- *
- *
- * Violations in the form data usually have property paths like:
- *
- *
- * data.firstName
- * data.Address.street
- * ...
- *
- *
- * @param FieldInterface $field
- * @param PropertyPath $path
- * @param ConstraintViolation$violation
- */
- public function addError($message, PropertyPath $path = null, $type = null);
+ /**
+ * Recursively adds constraint violations to the fields
+ *
+ * Violations in the form fields usually have property paths like:
+ *
+ *
+ * iterator[firstName].data
+ * iterator[firstName].displayedData
+ * iterator[Address].iterator[street].displayedData
+ * ...
+ *
+ *
+ * Violations in the form data usually have property paths like:
+ *
+ *
+ * data.firstName
+ * data.Address.street
+ * ...
+ *
+ *
+ * @param FieldInterface $field
+ * @param PropertyPath $path
+ * @param ConstraintViolation$violation
+ */
+ public function addError($message, PropertyPath $path = null, $type = null);
- /**
- * Renders this field.
- *
- * @param array $attributes The attributes to include in the rendered
- * output
- * @return string The rendered output of this field
- */
- public function render(array $attributes = array());
+ /**
+ * Renders this field.
+ *
+ * @param array $attributes The attributes to include in the rendered
+ * output
+ * @return string The rendered output of this field
+ */
+ public function render(array $attributes = array());
- /**
- * Renders the errors of this field.
- *
- * @return string The rendered output of the field errors
- */
- public function renderErrors();
+ /**
+ * Renders the errors of this field.
+ *
+ * @return string The rendered output of the field errors
+ */
+ public function renderErrors();
- /**
- * Returns whether the field is bound.
- *
- * @return boolean
- */
- public function isBound();
+ /**
+ * Returns whether the field is bound.
+ *
+ * @return boolean
+ */
+ public function isBound();
- /**
- * Returns whether the field is valid.
- *
- * @return boolean
- */
- public function isValid();
+ /**
+ * Returns whether the field is valid.
+ *
+ * @return boolean
+ */
+ public function isValid();
- /**
- * Returns whether the field requires a multipart form.
- *
- * @return boolean
- */
- public function isMultipart();
+ /**
+ * Returns whether the field requires a multipart form.
+ *
+ * @return boolean
+ */
+ public function isMultipart();
- /**
- * Returns whether the field is required to be filled out.
- *
- * If the field has a parent and the parent is not required, this method
- * will always return false. Otherwise the value set with setRequired()
- * is returned.
- *
- * @return boolean
- */
- public function isRequired();
+ /**
+ * Returns whether the field is required to be filled out.
+ *
+ * If the field has a parent and the parent is not required, this method
+ * will always return false. Otherwise the value set with setRequired()
+ * is returned.
+ *
+ * @return boolean
+ */
+ public function isRequired();
- /**
- * Returns whether this field is disabled
- *
- * The content of a disabled field is displayed, but not allowed to be
- * modified. The validation of modified, disabled fields should fail.
- *
- * Fields whose parents are disabled are considered disabled regardless of
- * their own state.
- *
- * @return boolean
- */
- public function isDisabled();
+ /**
+ * Returns whether this field is disabled
+ *
+ * The content of a disabled field is displayed, but not allowed to be
+ * modified. The validation of modified, disabled fields should fail.
+ *
+ * Fields whose parents are disabled are considered disabled regardless of
+ * their own state.
+ *
+ * @return boolean
+ */
+ public function isDisabled();
- /**
- * Returns whether the field is hidden
- *
- * @return boolean
- */
- public function isHidden();
+ /**
+ * Returns whether the field is hidden
+ *
+ * @return boolean
+ */
+ public function isHidden();
- /**
- * Sets whether this field is required to be filled out when submitted.
- *
- * @param boolean $required
- */
- public function setRequired($required);
+ /**
+ * Sets whether this field is required to be filled out when submitted.
+ *
+ * @param boolean $required
+ */
+ public function setRequired($required);
- /**
- * Sets the generator used for rendering HTML.
- *
- * Usually there is one generator instance shared between all fields of a
- * form.
- *
- * @param string $charset
- */
- public function setGenerator(HtmlGeneratorInterface $generator);
+ /**
+ * Sets the generator used for rendering HTML.
+ *
+ * Usually there is one generator instance shared between all fields of a
+ * form.
+ *
+ * @param string $charset
+ */
+ public function setGenerator(HtmlGeneratorInterface $generator);
}
\ No newline at end of file
diff --git a/src/Symfony/Components/Form/Form.php b/src/Symfony/Components/Form/Form.php
index 6bd0892db1..8d7a9dd017 100644
--- a/src/Symfony/Components/Form/Form.php
+++ b/src/Symfony/Components/Form/Form.php
@@ -34,528 +34,491 @@ use Symfony\Components\File\UploadedFile;
*/
class Form extends FieldGroup
{
- protected static $defaultCsrfSecret = null;
- protected static $defaultCsrfProtection = false;
- protected static $defaultCsrfFieldName = '_csrf_token';
- protected static $defaultLocale = null;
- protected static $defaultTranslator = null;
+ protected static $defaultCsrfSecret = null;
+ protected static $defaultCsrfProtection = false;
+ protected static $defaultCsrfFieldName = '_csrf_token';
+ protected static $defaultLocale = null;
+ protected static $defaultTranslator = null;
- protected $validator = null;
- protected $validationGroups = null;
+ protected $validator = null;
+ protected $validationGroups = null;
- private $csrfSecret = null;
- private $csrfFieldName = null;
+ private $csrfSecret = null;
+ private $csrfFieldName = null;
- /**
- * Constructor.
- *
- * @param array $defaults An array of field default values
- * @param array $options An array of options
- * @param string $defaultCsrfSecret A Csrf secret
- */
- public function __construct($name, $object, ValidatorInterface $validator, array $options = array())
- {
- $this->generator = new HtmlGenerator();
- $this->validator = $validator;
-
- parent::__construct($name, $options);
-
- $this->setData($object);
- $this->setCsrfFieldName(self::$defaultCsrfFieldName);
-
- if (self::$defaultCsrfSecret !== null)
+ /**
+ * Constructor.
+ *
+ * @param array $defaults An array of field default values
+ * @param array $options An array of options
+ * @param string $defaultCsrfSecret A Csrf secret
+ */
+ public function __construct($name, $object, ValidatorInterface $validator, array $options = array())
{
- $this->setCsrfSecret(self::$defaultCsrfSecret);
- }
- else
- {
- $this->setCsrfSecret(md5(__FILE__.php_uname()));
- }
+ $this->generator = new HtmlGenerator();
+ $this->validator = $validator;
- if (self::$defaultCsrfProtection !== false)
- {
- $this->enableCsrfProtection();
- }
+ parent::__construct($name, $options);
- if (self::$defaultLocale !== null)
- {
- $this->setLocale(self::$defaultLocale);
- }
+ $this->setData($object);
+ $this->setCsrfFieldName(self::$defaultCsrfFieldName);
- if (self::$defaultTranslator !== null)
- {
- $this->setTranslator(self::$defaultTranslator);
- }
- }
-
- /**
- * Sets the charset used for rendering HTML
- *
- * This method overrides the internal HTML generator! If you want to use
- * your own generator, use setGenerator() instead.
- *
- * @param string $charset
- */
- public function setCharset($charset)
- {
- $this->setGenerator(new HtmlGenerator($charset));
- }
-
- /**
- * Sets the validation groups for this form.
- *
- * @param array|string $validationGroups
- */
- public function setValidationGroups($validationGroups)
- {
- $this->validationGroups = $validationGroups === null ? $validationGroups : (array) $validationGroups;
- }
-
- /**
- * Returns the validation groups for this form.
- *
- * @return array
- */
- public function getValidationGroups()
- {
- return $this->validationGroups;
- }
-
- /**
- * Sets the default locale for newly created forms.
- *
- * @param string $defaultLocale
- */
- static public function setDefaultLocale($defaultLocale)
- {
- self::$defaultLocale = $defaultLocale;
- }
-
- /**
- * Returns the default locale for newly created forms.
- *
- * @return string
- */
- static public function getDefaultLocale()
- {
- return self::$defaultLocale;
- }
-
- /**
- * Sets the default translator for newly created forms.
- *
- * @param TranslatorInterface $defaultTranslator
- */
- static public function setDefaultTranslator(TranslatorInterface $defaultTranslator)
- {
- self::$defaultTranslator = $defaultTranslator;
- }
-
- /**
- * Returns the default translator for newly created forms.
- *
- * @return TranslatorInterface
- */
- static public function getDefaultTranslator()
- {
- return self::$defaultTranslator;
- }
-
- /**
- * Binds the form with values and files.
- *
- * This method is final because it is very easy to break a form when
- * overriding this method and adding logic that depends on $taintedFiles.
- * You should override doBind() instead where the uploaded files are
- * already merged into the data array.
- *
- * @param array $taintedValues The form data of the $_POST array
- * @param array $taintedFiles The form data of the $_FILES array
- * @return boolean Whether the form is valid
- */
- final public function bind($taintedValues, array $taintedFiles = null)
- {
- if ($taintedFiles === null)
- {
- if ($this->isMultipart() && $this->getParent() === null)
- {
- throw new \InvalidArgumentException('You must provide a files array for multipart forms');
- }
-
- $taintedFiles = array();
- }
- else
- {
- $taintedFiles = self::convertFileInformation(self::fixPhpFilesArray($taintedFiles));
- }
-
- $this->doBind(self::deepArrayUnion($taintedValues, $taintedFiles));
-
- if ($this->getParent() === null)
- {
- if ($violations = $this->validator->validate($this, $this->getValidationGroups()))
- {
- foreach ($violations as $violation)
- {
- $propertyPath = new PropertyPath($violation->getPropertyPath());
-
- if ($propertyPath->getCurrent() == 'data')
- {
- $type = self::DATA_ERROR;
- $propertyPath->next(); // point at the first data element
- }
- else
- {
- $type = self::FIELD_ERROR;
- }
-
- $this->addError($violation->getMessage(), $propertyPath, $type);
+ if (self::$defaultCsrfSecret !== null) {
+ $this->setCsrfSecret(self::$defaultCsrfSecret);
+ } else {
+ $this->setCsrfSecret(md5(__FILE__.php_uname()));
}
- }
- }
- }
- /**
- * Binds the form with the given data.
- *
- * @param array $taintedData The data to bind to the form
- * @return boolean Whether the form is valid
- */
- protected function doBind(array $taintedData)
- {
- parent::bind($taintedData);
- }
+ if (self::$defaultCsrfProtection !== false) {
+ $this->enableCsrfProtection();
+ }
- /**
- * Gets the stylesheet paths associated with the form.
- *
- * @return array An array of stylesheet paths
- */
- public function getStylesheets()
- {
- return $this->getWidget()->getStylesheets();
- }
+ if (self::$defaultLocale !== null) {
+ $this->setLocale(self::$defaultLocale);
+ }
- /**
- * Gets the JavaScript paths associated with the form.
- *
- * @return array An array of JavaScript paths
- */
- public function getJavaScripts()
- {
- return $this->getWidget()->getJavaScripts();
- }
-
- /**
- * Returns a CSRF token for the set CSRF secret
- *
- * If you want to change the algorithm used to compute the token, you
- * can override this method.
- *
- * @param string $secret The secret string to use (null to use the current secret)
- *
- * @return string A token string
- */
- protected function getCsrfToken()
- {
- return md5($this->csrfSecret.session_id().get_class($this));
- }
-
- /**
- * @return true if this form is CSRF protected
- */
- public function isCsrfProtected()
- {
- return $this->has($this->getCsrfFieldName());
- }
-
- /**
- * Enables CSRF protection for this form.
- */
- public function enableCsrfProtection()
- {
- if (!$this->isCsrfProtected())
- {
- $field = new HiddenField($this->getCsrfFieldName(), array(
- 'property_path' => null,
- ));
- $field->setData($this->getCsrfToken());
- $this->add($field);
- }
- }
-
- /**
- * Disables CSRF protection for this form.
- */
- public function disableCsrfProtection()
- {
- if ($this->isCsrfProtected())
- {
- $this->remove($this->getCsrfFieldName());
- }
- }
-
- /**
- * Sets the CSRF field name used in this form
- *
- * @param string $name The CSRF field name
- */
- public function setCsrfFieldName($name)
- {
- $this->csrfFieldName = $name;
- }
-
- /**
- * Returns the CSRF field name used in this form
- *
- * @return string The CSRF field name
- */
- public function getCsrfFieldName()
- {
- return $this->csrfFieldName;
- }
-
- /**
- * Sets the CSRF secret used in this form
- *
- * @param string $secret
- */
- public function setCsrfSecret($secret)
- {
- $this->csrfSecret = $secret;
- }
-
- /**
- * Returns the CSRF secret used in this form
- *
- * @return string
- */
- public function getCsrfSecret()
- {
- return $this->csrfSecret;
- }
-
- /**
- * Returns whether the CSRF token is valid
- *
- * @return boolean
- */
- public function isCsrfTokenValid()
- {
- if (!$this->isCsrfProtected())
- {
- return true;
- }
- else
- {
- return $this->get($this->getCsrfFieldName())->getDisplayedData() === $this->getCsrfToken();
- }
- }
-
- /**
- * Enables CSRF protection for all new forms
- */
- static public function enableDefaultCsrfProtection()
- {
- self::$defaultCsrfProtection = true;
- }
-
- /**
- * Disables Csrf protection for all forms.
- */
- static public function disableDefaultCsrfProtection()
- {
- self::$defaultCsrfProtection = false;
- }
-
- /**
- * Sets the CSRF field name used in all new CSRF protected forms
- *
- * @param string $name The CSRF field name
- */
- static public function setDefaultCsrfFieldName($name)
- {
- self::$defaultCsrfFieldName = $name;
- }
-
- /**
- * Returns the default CSRF field name
- *
- * @return string The CSRF field name
- */
- static public function getDefaultCsrfFieldName()
- {
- return self::$defaultCsrfFieldName;
- }
-
- /**
- * Sets the CSRF secret used in all new CSRF protected forms
- *
- * @param string $secret
- */
- static public function setDefaultCsrfSecret($secret)
- {
- self::$defaultCsrfSecret = $secret;
- }
-
- /**
- * Returns the default CSRF secret
- *
- * @return string
- */
- static public function getDefaultCsrfSecret()
- {
- return self::$defaultCsrfSecret;
- }
-
- /**
- * Renders the form tag.
- *
- * This method only renders the opening form tag.
- * You need to close it after the form rendering.
- *
- * This method takes into account the multipart widgets.
- *
- * @param string $url The URL for the action
- * @param array $attributes An array of HTML attributes
- *
- * @return string An HTML representation of the opening form tag
- */
- public function renderFormTag($url, array $attributes = array())
- {
- return sprintf('