Due to the fact, that the Page controller of the APF provides a generic frame to handle taglibs, we were able to implement a form abstraction built on taglibs (e.g. text fields).
The release of the adventure php framework contains a set of taglibs, that fully describe HTML forms and that contain features such as filtering, validation and presetting at the same time out of the box. With the taglibs reworked with release 1.11, form fields can be applied various filters and validators and custom form elements can be integrated with the delivered ones.
The subsequent chapters describe the general design and the handling of APF forms as well as the tags shipped with the release with their meaning and functionality. Reading this documentation and implementing applications, it is recommended to have the API documentation opened. There, you can see, which functionality is included in the various taglibs - e.g. the HtmlFormTag.
<core:addtaglib
namespace="tools::form::taglib"
class="HtmlFormTag"
prefix="html"
name="form"
/>
<html:form name="Search">
<form:text name="searchterm" /> <form:button name="search" value="GO" />
</html:form>class search_controller extends BaseDocumentController {
public function transformContent(){
$form = &$this->getForm('Search');
$form->transformOnPlace();
}
}The subsequent sections present the form controls contained within the APF release and give you a brief introduction to the usage of these elements.
The APF contains an extended set of form control abstraction tags, that can be configured for different purposes. In case this is not enough, custom form taglibs can be introduced as desired. This is described in detail under Usage of forms.
Since the APF's page controller is built up on a generic tag parser, form tags can be added generic attributes such as:
These are presented in the generated source code and can thus be used for formatting purposes. For this reason, the following chapters do not mention these attributes, but only describe the attributes, that are necessary for the configuration of the tag.
The form tag itself takes three functional atributes, that are used for definition and configuration:
<html:form name="" [method=""] [action=""] [autocomplete="true|false"]>
...
</html:form>[A-Za-z0-9-_])
[get|post])
Registry::register('apf::tools', 'FormDefaultMethod','...')<form:button name="" value="" />
<form:button name="">
<button:getstring
[name=""]
namespace=""
config=""
entry="" />
</form:button>[A-Za-z0-9-_])
The image button tag creates an image button, that uses an image as the label. The functionality is equal to the "normal" button.
<form:imagebutton name="" src="" />[A-Za-z0-9-_])
<form:reset name="" value="" /><form:hidden name="" value="" />[A-Za-z0-9-_])
<form:text name="" [value=""] />[A-Za-z0-9-_])
<form:area name="" />
<form:area name="">...</form:area>[A-Za-z0-9-_])
<form:password name="" [value=""]/>[A-Za-z0-9-_])
The file upload field can be defined as follows:
<form:file name="" />[A-Za-z0-9-_])
As of release 1.12, the file upload field was enhanced to support the developer to build file upload forms easily. For this reason, the taglib was enhanced by the following methods that can be used in document controllers to handle uploaded files:
The wiki article File-Upload mit Form-Taglibs (German) describes, how file upload can now be realized easily. Please note the MimeTypeValidator and FileSizeValidator added in 1.12. They can be used to validate the files uploaded by the user.
<form:checkbox name="" value="" [checked="checked"] />[A-Za-z0-9-_])
<form:radio name="" value="" [checked="checked"]/>[A-Za-z0-9-_])
A select field can be defined using multiple approaches: using static or dynamic options and/or group or a combination of both ways. Dynamic options and groups can be added or processed using a (Document-)Controller.
Since release 1.12 option groups are fully supported by the APF's form taglibs.
<form:select name="" />
<form:select name="">
<select:option value="" [selected="selected"]>...</select:option>
[<select:group label="">
<group:option value="" [selected="selected"]>...</group:option>
</select:group>]
</form:select>[A-Za-z0-9-_])
[A-Za-z0-9-_])
As the simple select field, the multi select field allows you to specify two different types: with and without static options.
<form:multiselect name="" />
<form:multiselect name="">
<select:option value="" [selected="selected"]></select:option>
[<select:group label="">
<group:option value="" [selected="selected"]>...</group:option>
<select:group>]
</form:multiselect>[A-Za-z0-9-_])
[A-Za-z0-9-_])
<form:placeholder name="" />[A-Za-z0-9-_])
$form = &$this->getForm('MyForm');
$form->setPlaceHolder('NameOfThePlaceHolder','...Value...');To ease date select field generation, the APF contains a date form control that can be configured to fit your needs.
<form:date
name=""
[yearrange=""]
[offsetnames=""]
[tab-indexes=""]
[prepend-empty-options="true|false"]
/>[A-Za-z0-9-_])
1990-2007.
(Allowed characters: [0-9-])
day;month;year.
(Allowed characters: [A-Za-z;])
1;2;3.
(Allowed characters: [0-9;])
true|false)
As described in chapter dynamic forms, the <form:marker /> tag can be used to position dynamic form elements. The tag itself creates no output.
<form:marker name="" />[A-Za-z0-9_-])
<form:addtaglib namespace="" class="" prefix="" name="" />[A-Za-z0-9_-:])
[a-z])
[a-z])
<form:getstring [name=""] namespace="" config="" entry="" />[A-Za-z0-9_-])
[A-Za-z0-9_-:])
[A-Za-z0-9-_])
[A-Za-z0-9-_.])
<form:listener [name=""] control="..." [validator="..." ]>
...
[<listener:getstring namespace="" config="" entry="" />]
[<listener:placeholder name="" />]
[<listener:addtaglib namespace="" class="" prefix="" name=""/>]
...
</form:listener>[A-Za-z0-9_-])
[A-Za-z0-9_-])
The place holder tag <listener:placeholder /> acts like the <html:placeholder />, the <listener:getstring /> tag like the <html:getstring /> tag, and the <listener:addtaglib /> tag like the <core:addtaglib /> tag.
In order to fill the the place holder, the following document controller code can be used:
Template:<html:form name="name_form" method="post">
<form:listener name="name-listener" control="name">
Please fill in the <listener:placeholder name="field-name" /> field!
</form:listener>
<form:text name="name" />
<form:addvalidator
class="TextLengthValidator"
control="name"
button="send"
/>
<form:button name="send" value="Send" />
</html:form>class form_controller extends BaseDocumentController {
public function transformContent(){
$form = &$this->getForm('name-form');
$listener = &$form->getFormElementByName('name-listener');
$listener->setPlaceHolder('field-name','name');
}
}The attribute validator can be used in case the form control to validate has a special validator defined. One sample application case is an email field, that should be marked up with two different error messages: one for missing input and one for incorrect email addresses. To achieve this, you can use the following template code:
<html:form name="email_form" method="post">
<form:listener control="email" validator="TextLengthValidator">
Please fill in the email field!
</form:listener>
<form:listener control="email" validator="EMailValidator">
Please fill in the email field with a correct email address!
</form:listener>
<form:text name="email" />
<form:addvalidator
class="TextLengthValidator"
control="name"
button="send"
type="special"
/>
<form:addvalidator
class="EMailValidator"
control="email"
button="send"
type="special"
/>
<form:button name="send" value="Send" />
</html:form><html:form name="email_form" method="post">
<form:listener control="email">
<div class="error-container">
</form:listener>
<form:listener control="email" validator="TextLengthValidator">
Please fill in the email field!
</form:listener>
<form:listener control="email" validator="EMailValidator">
Please fill in the email field with a correct email address!
</form:listener>
<form:listener control="email">
</div>
</form:listener>
<form:text name="email" />
<form:addvalidator
class="TextLengthValidator"
control="name"
button="send"
type="special"
/>
<form:addvalidator
class="EMailValidator"
control="email"
button="send"
type="special"
/>
<form:addvalidator
class="EMailValidator"
control="email"
button="send"
/>
<form:button name="send" value="Send" />
</html:form><form:error [name=""]>
...
[<error:getstring namespace="" config="" entry="" />]
[<error:placeholder name="" />]
[<error:addtaglib namespace="" class="" prefix="" name="" />]
...
</form:error>The place holder tag <error:placeholder /> acts like the <html:placeholder />, the <error:getstring /> tag like the <html:getstring /> tag, and the <error:addtaglib /> tag like the <core:addtaglib /> tag. Filling the place holder can be done as described in the last chapter.
<form:success [name=""]>
...
[<success:getstring namespace="" config="" entry="" />]
[<success:placeholder name="" />]
[<success:addtaglib namespace="" class="" prefix="" name="" />]
...
</form:success>The place holder tag <success:placeholder /> acts like the <html:placeholder />, the <success:getstring /> tag like the <html:getstring /> tag, and the <success:addtaglib /> tag like the <core:addtaglib /> tag. Filling the place holder can be done as described in the last chapter.
The TimeCaptcha can be used in combination with the TimeCaptchaValidator to defeat (spam-)bots in forms. For this reason, the taglib saves the point in time, the form is generated and makes this timestamp available for the validator within the session.
Setting the optional attribute seconds the minimal time can be influenced that the validator uses to decide, whether the form was filled by a bot or a regular user. The value of the attribute must contain an integer value of the desired period of time.
<html:form name="...">
<form:timecaptcha name="timecaptcha" [seconds="3"]/>
...
</html:form>This taglib creates a hidden field that contains a generated hash key. In case the hash sent with the form differs from the calculated hash, the form is marked invalid. Doing so, the form can be protected from CSRF attacks.
Hash generation is delegated to the configured component concerning the provider pattern. This makes it easy to change the algorithm. To customize the hash key generation, the CSRFHashProvider interface must be implemented that requires the generateHash() to be implemented.
The subsequent code box shows the provider that is already included in the APF:
class EncryptedSIDHashProvider extends APFObject implements CSRFHashProvider {
public function generateHash($salt) {
if(!defined('SID')) {
session_start();
}
return md5($salt.SID);
}
}The provider to use can be specified with the namespace and class attributes. In case the attributes are not present the default provider is used. The EncryptedSIDHashProvider creates an MD5 hash concatenating the session id and the configured salt.
<form:csrfhash
salt=""
name=""
[namespace=""
class=""]
/>To ease creation of time selection controls the APF provides the time control. This form control can be configured as desired using the following attributes:
<form:time
name=""
[hoursrange=""]
[offsetnames=""]
[showseconds="true|false"]
[minutesinterval=""]
/>Since release of APF version 1.16 creating an upload is more easier with the new tool MultiFileUpload. For more information about this tool read the instructions in the Wiki (German only): MultiFileUpload im Wiki
Since release 1.17 you can define form control labels as APF tag.
The label tag is defined as follows:
<form:label for="">
[...]
[<label:getstring [name=""] namespace="" config="" entry="" />]
</form:label>In the previous versions of the APF the validation was linked directly with a form element and the validation was executed within the lifecycle of the taglibs. This has the disadvantage, that one element can be defined with only one validator. This leads to redundant code, because each field must have it's own validator defined. Furthermore, it was not possible to implement own validators.
From version 1.11 these circumstances were considered and the form taglibs were moved to a new variation of the validation. From this version validators are applied by an own tag, that follows the observer pattern. The tag itself then binds a validator to the desired form control. Thus it is possible to attach several validators onto one field, to write own validators and to validate several fields with one observer definition at the same time.
The definition of a validator now is as follows:
<form:addvalidator
namespace=""
class=""
button=""
control=""
[type="special"]
/>[A-Za-z0-9:])
[A-Za-z0-9_])
[A-Za-z0-9-_])
[A-Za-z0-9-_|])
special)
Within release 1.12 the marking of invalid form controls has been switched to css classes. This eases formatting and is much more flexible. The implementation proposal can be read about on the Weiterentwicklung Formular-Validierung (German) page.
By default, those form controls that are marked invalid by validators are added the apf-form-error css class. In case you want to use your own css class just add the valmarkerclass attribute for the appropriate form control:
<form:text
name="age"
valmarkerclass="special-val-marker"
/>In order to optically mark all invalid form controls within your web project having a red border add the subsequent style definition to yout css files:
.apf-form-error {
border: 2px solid red;
}Please note, that special css marker class must be contained in your css definition, too. Otherwise, an invalid field may not be marked optically.
As in previous releases, the APF shipps a couple of built-in validators. These usually cover the large part of the demands. Should these not be enough, own validators can be implemented. This is described in the next chapter.
<html:form name="...">
<form:text name="age" optional="true"/>
<form:addvalidator
class="IntegerValidator"
button="send"
control="age"
/>
<form:button name="send" value="Send" />
</html:form>Currently the following validators are available:
The TextLengthValidator checks a text form field (text field, password field, text area) whether the contained text has a least length. By default, the text entered by the user must have a least length of 3 characters, before the field is considered valid. Provided that another text length is desired, the attributes minlength and maxlength must be given in the attribute list of the referenced text field:
<html:form name="...">
<form:text name="firstname" />
<form:text name="lastname" minlength="5" />
<form:password name="pass" />
<form:area name="comment" minlength="20" maxlength="200"/>
<form:button name="send" value="Send"/>
<form:addvalidator
class="TextLengthValidator"
button="send"
control="firstname|lastname|pass|comment"
/>
</html:form>Within the code block the form control firstname is tested to contain at least >=3 characters, the lastname field must contain at least 5 characters and at maximum 200 characters to be considered valid.
Since release 1.12 it is possible to enable strict validation of the text length. To do so, you can add the mode attribute to the target control. In case it is filled with strict the input is filtered through trim() to accept content that does not only contain blanks.
<html:form name="...">
<form:text name="number" />
<form:button name="send" value="Send"/>
<form:addvalidator
class="NumberValidator"
button="send"
control="number"
/>
</html:form><html:form name="...">
<form:text name="email" />
<form:button name="send" value="Send"/>
<form:addvalidator
class="EMailValidator"
button="send"
control="email"
/>
</html:form><html:form name="...">
<form:text name="phone" />
<form:text name="fax" />
<form:button name="send" value="Send"/>
<form:addvalidator
class="PhoneAndFaxValidator"
button="send"
control="phone|fax"
/>
</html:form><html:form name="...">
<form:password name="pass" ref="pass2" />
<form:password name="pass2" />
<form:addvalidator
class="FieldCompareValidator"
control="pass"
button="login"
/>
<form:button name="login" value="login" />
</html:form><html:form name="...">
<form:text name="birthday" />
<form:button name="send" value="Send"/>
<form:addvalidator
class="SimpleBirthdayValidator"
button="send"
control="birthday"
/>
</html:form><html:form name="...">
<form:select name="color">
<select:option value=""></select:option>
<select:option value="red">Red color</select:option>
<select:option value="green">Green color</select:option>
</form:select>
<form:button name="send" value="Send"/>
<form:addvalidator
class="SimpleSelectControlValidator"
button="send"
control="color"
/>
</html:form><html:form name="...">
<form:multiselect name="colors">
<select:option value="red">Red color</select:option>
<select:option value="green">Green color</select:option>
</form:multiselect>
<form:button name="send" value="Send"/>
<form:addvalidator
class="MultiSelectFieldValidator"
button="send"
control="colors"
/>
</html:form><html:form name="...">
<form:radio id="red" name="color" /> Red color
<form:radio id="green" name="color" /> Green color
<form:radio id="blue" name="color" /> Blue color
<form:button name="send" value="Send"/>
<form:addvalidator
class="SimpleRadioControlValidator"
button="send"
control="color"
/>
</html:form><html:form name="...">
<form:date name="birthday" />
<form:button name="send" value="Send"/>
<form:addvalidator
class="SimpleDateControlValidator"
button="send"
control="birthday"
/>
</html:form>The MimeTypeValidator checks, whether the MIME type of the uploaded file can be found in the list of accepted file types. This list must be defines within a pipe-separated list in the accepts attribute of the form control to validate.
The evaluation of the file type is done using the finfo starting at PHP 5.3. For older versions, the type offset of the $_FILES is used (please refer to $_FILES in the PHP manual).
The subsequently defined form accepts only PDF files in the upload field. The form is marked as invalid as long as the uploaded file matches the accepted file format:
<html:form name="upload" method="post">
<fieldset>
<label for="pdf">PDF file:</label>
<form:file name="pdf" id="pdf" accepts="pdf|application/pdf" />
<form:addvalidator
class="MimeTypeValidator"
control="pdf"
button="send"
/>
<form:button name="send" value="GO" />
</fieldset>
</html:form>For compatibility reasons, the accepts attribute contains both the MIME type notation and the file extension. This makes an upgrade to PHP 5.3 more easy, because the functionality is still given after an upgrade.
The FileSizeValidator checks the file size of the uploaded file. For this reason, the optional attributes minsize And maxsize can be used to define the allowed file size in in bytes.
In case the attribute minsize is not defined in the target control's attribute list, 0 is taken as default value. This let's the form accept 0 byte files as valid files. In case the maxsize attribute is not defined in the target control's attribute list, a maximum size of 1024000 (=1 MB) is assumed.
The following upload field only allows files with at least 20kB and a maximum size of 500kB:
<html:form name="upload" method="post">
<fieldset>
<label for="image">Image:</label>
<form:file name="image" id="image" minsize="20480" maxsize="512000" />
<form:addvalidator
class="FileSizeValidator"
control="image"
button="send"
/>
<form:button name="send" value="GO" />
</fieldset>
</html:form>The CheckboxValidator validates if a checkbox is activated of not. In case it is not activated, the control is marked as invalid.
<html:form name="upload" method="post">
<fieldset>
...
<form:checkbox name="agb" value="agb" /> Accepting terms of conditions.
<form:addvalidator
class="CheckboxValidator"
button="send"
control="agb"
/>
<form:button name="send" value="GO" />
</fieldset>
</html:form>The TimeCaptchaValidator can be used in combination with the <form:timecaptcha /> taglib to defeat (spam-)bots in forms. The validator checks the time, which the "user" took to complete the form. Bots normally only need a fraction of a second, so it should be enough to refuse all data which was filled within the default 2 seconds. This period can be influenced by adding an optional seconds attribute in the taglib definition.
<html:form name="...">
<form:timecaptcha name="timecaptcha"/>
<form:addvalidator
class="TimeCaptchaValidator"
button="send"
control="timecaptcha"
/>
<form:button name="send" value="Send"/>
</html:form>The IntegerValidator validates whether the value entered is a valid integer number:
<html:form name="...">
<form:text name="age" />
<form:button name="send" value="Send"/>
<form:addvalidator
class="IntegerValidator"
button="send"
control="age"
/>
</html:form>The NumberScopeValidator validates a form control to contain an integer within a dedicated range. The default range is from 0 to 65535 (maximum number for 16-bit integers). The lower and upper limit can be configured with the minvalue and maxvalue attributes with the form control. The lower and upper limit can be set to unlimited applying value null.
<html:form name="...">
<form:text name="age" minvalue="10" maxvalue="100" />
<form:button name="send" value="Send"/>
<form:addvalidator
class="NumberScopeValidator"
button="send"
control="age"
/>
</html:form>Validators are classes, that are derived from AbstractFormValidator. This abstract base class defines the structure of all form validators. This is necessary to be able to apply all validators to form controls as an observer.
The interface is as follows:
abstract class AbstractFormValidator extends APFObject {
protected $control;
protected $button;
public function AbstractFormValidator(AbstractFormControl &$control, AbstractFormControl &$button){
$this->control = &$control;
$this->button = &$button;
}
public abstract function validate($input);
public abstract function notify();
public function isActive(){
return $this->button->isSent();
}
}public function addValidator(AbstractFormValidator &$validator){
if($validator->isActive()){
if(!$validator->validate($this->getAttribute('value'))){
$validator->notify();
}
}
}<form:addfilter
namespace=""
class=""
button=""
control=""
/>[A-Za-z0-9:])
[A-Za-z0-9_])
[A-Za-z0-9-_])
[A-Za-z0-9-_|])
As in previous releases, the APF ships a couple of built-in filters. These usually cover the large part of the demands. Should these not be enough, own filters can be implemented. This is described in the next chapter.
Starting with release 1.14 the regular expressions of various filters can be adapted by defining the expression within the filter-expr tag attribute on the target form control. This helps to avoid character set issues as discussed within the Forum (German).
The subsequently listed filters do support overwriting the filter regexp:
Further, charset sensitive function calls use the charset globally defined within the Registry.
Currently the following filters are available:
<html:form name="...">
<form:text name="email" />
<form:button name="send" value="Send"/>
<form:addfilter
class="EMailFilter"
button="send"
control="email"
/>
</html:form><html:form name="...">
<form:text name="simplechars" />
<form:button name="send" value="Send"/>
<form:addfilter
class="NoSpecialCharactersFilter"
button="send"
control="simplechars"
/>
</html:form><html:form name="...">
<form:text name="htmlentitiestext" />
<form:button name="send" value="Send"/>
<form:addfilter
class="OnlyHTMLEntitiesFilter"
button="send"
control="htmlentitiestext"
/>
</html:form><html:form name="...">
<form:text name="zipcode" />
<form:button name="send" value="Send"/>
<form:addfilter
class="OnlyIntegersFilter"
button="send"
control="zipcode"
/>
</html:form><html:form name="...">
<form:text name="productcode" />
<form:button name="send" value="Send"/>
<form:addfilter
class="OnlyLettersFilter"
button="send"
control="productcode"
/>
</html:form><html:form name="...">
<form:text name="productprice" />
<form:button name="send" value="Send"/>
<form:addfilter
class="OnlyNumbersFilter"
button="send"
control="productprice"
/>
</html:form><html:form name="...">
<form:area name="simpletext" />
<form:button name="send" value="Send"/>
<form:addfilter
class="SpecialCharacterFilter"
button="send"
control="simpletext"
/>
</html:form><html:form name="...">
<form:area name="lowertext" />
<form:button name="send" value="Send"/>
<form:addfilter
class="String2LowerFilter"
button="send"
control="lowertext"
/>
</html:form><html:form name="...">
<form:area name="uppertext" />
<form:button name="send" value="Send"/>
<form:addfilter
class="String2LowerFilter"
button="send"
control="uppertext"
/>
</html:form><html:form name="...">
<form:area name="notagstext" />
<form:button name="send" value="Send"/>
<form:addfilter
class="StripTagsFilter"
button="send"
control="notagstext"
/>
</html:form>The FloatFilter can be used to convert the content of a text field into a floating number.
<html:form name="...">
<form:area name="length" />
<form:button name="send" value="Send"/>
<form:addfilter
class="FloatFilter"
button="send"
control="length"
/>
</html:form>Form filters are special filters, that are built on the APF filter API. Each form filter must implement the AbstractFormFilter class that acts as an interface for dedicated form filter implementations.
Just like the form validators, form filters must implement some form-specific methods. The AbstractFormFilter interface class is as follows:
abstract class AbstractFormFilter extends AbstractFilter {
protected $control;
protected $button;
public function AbstractFormFilter(AbstractFormControl &$control, AbstractFormControl &$button){
$this->control = &$control;
$this->button = &$button;
}
public function isActive(){
return $this->button->isSent();
}
}public function addFilter(AbstractFormFilter &$filter){
if($filter->isActive()){
$value = $this->getAttribute('value');
$filteredValue = $filter->filter($value);
$this->setAttribute('value',$filteredValue);
}
}As mentioned in the last chapters, a filter must inherit from the AbstractFormFilter class.
In order to implement custom filters, please remember the following topics:
Chapter Usage of forms deals with the usage of APF forms and the form handling using document controller. The present page is only intended as a reference for the shipped tags and the implementation of validators and filters. Further, the linked chapter discusses dynamic form implementation.
JetBRAINS supports the development of the APF with PHPStorm licenses and we feel confidential that PHPStorm strongly influences the APF's quality. Use PHPStorm!
Proud to useIntelligent PHP IDE for coding, testing and debugging with pleasure