As already mentioned, TypoScript is commonly used to map values
to a hierarchy - a tree of objects. This object tree is a central
concept used in the TYPO3 CMS. The branches of the tree are indicated
with periods "." – a syntax which can also be found
in some languages such as JavaScript and conveys the idea of defining
objects and their properties. The equal sign "=" is
used for assigning values or objects types and not, as you might
suppose, to compare values.
The example above demonstrates how an object is defined and how a value is assigned to a property.
The object name identifies the new object and can be freely chosen. It must consist of a combination of the following characters: A-Z, a-z, 0-9 | |
An object must be of a specific type. Which types are available depends on the context where the TypoScript code is used. We'll have a closer look at these contexts, soon. | |
Values can be assigned to the properties of an object by referring to the property's name. Which properties exist, depends on the object type. |
Too abstract? Then let's be more specific and have a look at a real world example:
In these two lines we define a new object called
myText which is of the type
Text and assign the value Hello
world! to the a property called
value.
Objects can be easily be grouped into branches of the object tree. Consider the following illustration:
The following TypoScript would be neccessary to created the above tree:
Example 3.6. TS code for the simple object tree
library.boilerplates.greeting = Text library.boilerplates.greeting.value = "Hello world" library.boilerplates.footnote = Text library.boilerplates.footnote.value = "©2007 by the TYPO3 guys"
TypoScript is parsed in a very simple way; line by line. This means that each line normally contains three parts based on the following formula:
[object path]
[operator]
[value]
The object path is a single object name or a path to that object, defined by several object names separated by periods "."
An operator is one of the following: "=", "<", ">", "=<". They are described later in this section.
The value can be one of the following:
a literal string enclosed by single or double quotes.
Single- and double quotes as well as the backslash and the
dollar sign must be escaped with a backslash sign. Examples:
"Hello world" or "and I said
\"Hello world\""
a number. Example: 23
a constant, written in all UPPERCASE. Examples:
BLUE or
MY_SPECIAL_CONSTANT
an object type, written in UpperCamelCase. Examples:
Text or
HierarchicalMenu
a variable or a combination of literals and variables.
Example: "Hello $name, nice to meet you!"
(the concept of variables will be explained in a later
section)
In the following example, myObject is defined
as a HTML content object and the property
value is set:
Example 3.7. A simple HTML content object
myObject = HTML myObject.value = "<div>some HTML code</div>"
The curly braces are used to assign many object properties in a simple way at once. It's called a confinement or nesting of properties.
Example 3.8. Confinements
# This definition ...
somePerson = Person
somePerson.firstName = "John"
somePerson.lastName = "Doe"
somePerson.address.street = "Rigensgade"
somePerson.address.city = "København"
# ... can be nicer structured with confinements:
somePerson = Person
somePerson {
firstName = "John"
lastName = "Doe"
address {
street = "Rigensgade"
city = "København"
}
} In general, the "=" operator is used to
assign values. As already mentioned, values can be literals, numbers,
constants, object types or variables. If a value already exists, it
will be overwritten.
Here are a few examples for each option:
Example 3.9. Assigning values
# Assigning object types: myObject = HTML someTextObject = Text aSpecialObject = SomeSpecializedContentObjectType # Assigning literals someTextObject.value = "Hello world!" myObject.value = " <- three spaces here" myObject.value = "Say \"hello\" to the world." xy.fileLocation = "c:\\mystuff\\things\\files\\cmd.exe" coffeeObject.espressoExtractionDuration = 25 # Assigning constants someTextObject.value = HELLO_WORLD_MESSAGE # Assigning variables someTextObject.value = $message
The ">" operator is used to unset whole
objects or single properties:
Example 3.10. Unsetting objects and properties
library.myObject = HTML library.myObject.value = "<strong>Hello world!</strong>" library.anotherObject = Text library.anotherObject.value = "Hi there" library.myObject > library.anotherObject.value >
The above example results in the following object tree:
The "<" operator is used to copy one
object or a whole object branch to another. The whole object is copied
(ie. cloned) - both, value and properties - and it overrides any
existing objects and values at the target position.
Example 3.11. Copying objects
myObject = HTML myObject.value = "<div>HTML code</div>" myOtherObject < myObject
In the above example the result is two independent sets of objects and their property which are exactly the same (duplicates). However they are not references to each other but independent copies:
It is common practice to copy objects, using them as a kind of template or boilerplate and then modifying only specific properties:
Example 3.12. Copying branches, overriding single properties
library.aLotOfObjects {
oneObject = SomeType
oneObject.firstProperty = "John"
oneObject.subObject.secondProperty = "Doe"
secondObject < .oneObject
}
myObject < library.aLotOfObjects
myObject.secondObject.firstProperty = "Johannes"Note the different way of referring to an object for the
secondObject: the .oneObject
refers to an object called oneObject in the same
branch or level of the tree. An alternative would have been to
write:
secondObject < library.aLotOfObjects.oneObject
In any case, the resulting object tree would look like this:
The "=<" operator is used to reference to
an object or a whole object branch. The whole object is not copied but
only referenced.
Example 3.13. Referencing objects
myObject = HTML myObject.value = "<div>HTML code</div>" myOtherObject =< myObject
In the above example the result is two independent object names refering to the identical object instance:
Let's see what happens if we take the example of the copy-operator and just use references instead:
Example 3.14. Referencing branches, overriding properties
library.aLotOfObjects {
oneObject = SomeType
oneObject.firstProperty = "John"
oneObject.subObject.secondProperty = "Doe"
secondObject =< .oneObject
}
myObject =< library.aLotOfObjects
myObject.secondObject.firstProperty = "Johannes"The resulting object tree looks quite different:
The "<<" operator
A powerful feature of TYPO3's TypoScript engine is the ability to process property values. Processors allow you to transform strings, add or combine information from elsewhere in the system or pass the current value to arbitrary user functions which take further action, depending on the content.
Probably the most popular processor function is the
wrap() method. It pre- and appends a string
to the property value:
Example 3.15. Using the wrap() value processor
myObject = Text
myObject.value = "I am so strong!"
myObject.value << 1.wrap("<strong>", "</strong>")The rendered output of this Text object
looks like this:
<strong>I am so strong!</strong>
The value property of the
Text object has been processed by the
wrap() function before it was displayed on the
website. Please note, that the processor does not modify the original
property – a second turn will deliver the exact same result.
Processors are called like functions in a programming language. In fact, a processor function is directly related to a PHP function of the same name which accepts (almost) the same arguments. Which arguments are expected, depends on the processor function (you find all details in the TypoScript Reference).
A good thing about processor functions is that you can run them in a chain: The first processor gets the original value and passes the processed value to the next processor which processes it and ...
Well, another good thing about processor functions is that all value processors are available for any property.
But before we wax lyrical about processors, let's add another
function. The crop() processor takes (at least)
two arguments: to which length a string should be cropped and if a
string should be appended.
Example 3.16. Two processors in a chain
myObject = Text
myObject.value = "I am so strong!"
myObject.value << 1.crop(8, "...")
myObject.value << 2.wrap("<strong>", "</strong>")And here is the result:
<strong>I am so ...</strong>
The order in which the processor functions are called is
determined by the numerical index (1 and
2 in the above example). Of course you can run a
wrap() function then a
crop() function, then a
wrap() function again and so on. There are a big
variety of processors available which you can run multiple times in
any order and medium-skilled PHP developers can easily create new ones
in a few minutes. And of course you can use variables as arguments for
processor functions.
The following example configures a Text
object which announces whether the current page has sub pages or not.
It uses two new processor functions: an if
function which takes a condition as an argument an returns either the
value of the second or third argument if the condition was true or
not. And a hasSubPages function which returns
TRUE if the current page has sub pages:
Example 3.17. Has sub pages or not
yesHasSubPagesMessage = Text yesHasSubPagesMessage.value = "Yes, this page has sub pages." myObject = Text myObject.$yesMessage < yesHasSubPagesMessage myObject.value << 1.if(hasSubPages(), $yesMessage, "No, this page has no sub pages."
This code surely could have been simplified - but we wanted to demonstrate the use of variables again. As you can see, the property value can even be processed if no value has been assigned earlier.
As already mentioned, you are not restricted to using the built-in processors of the CMS package but can create your own processors. The only requirement is that your public processor method accepts the subject (ie. the value to be processed) as the first argument and returns a string as the result. Once you have programmed your own processor, just call it by specifying the component and method name as seen in the next example:
Example 3.18. Calling user-defined processors
myObject = Text
myObject.value = "Creating processors is easy!"
myObject.value << 1.F3_MyPackage_MyProcessors->translate("spanish")The expected output of this TypoScript object is something like
¡Crear procesadores es facil! -
provided that you wrote a processor which is capable of translating
text.
![]() | Tip |
|---|---|
The best way to learn how easy it is to create your own
processors is browsing through the code of the functions provided by
the CMS package. Just check out the class
|
![]() | Note |
|---|---|
If you don't specify a component name but only refer to the
processor method name, the component name
|
When a line starts with "//" or
"#" it is considered a comment which means that the
line is totally ignored in the parsing process.
Example 3.19. Comments
// This is a comment myObject = HTML myObject.value = "<div>some HTML code</div>" # This line is also a comment
A block of comment is indicated by enclosing it into
"/*" and "". Anything within
that comment block will be ignored.*/
Example 3.20. Comment blocks
/* This is a comment ... and this line is within that comment which ... ends here: */ myObject = HTML myObject.value = "<div>some HTML code</div>"
In some cases it is handy to access a placeholder instead of
writing down a whole object definition multiple times. Imagine you're
working with boilerplates, some of them are regular
Text objects others might be literals, and now
you want to create a new object which combines these premade chunks of
text. One option to do that is using variables:
Example 3.21. Using variables
boilerplates.helloMessage = Text boilerplates.helloMessage.value = "Hello you!" myObject = Text myObject.$hello < boilerplates.helloMessage myObject.$goodbye = "Goodbye, see you soon!" myObject.value = "$hello<br />Nice that you\'re here again.<br />$goodbye"
After parsing and transforming the above TypoScript code into
PHP objects, the value of myObject would be
"Hello you!<br />Nice that you\'re here again.<br
/>Goodbye, see you soon!".
As you can see in the example, you create a variable just as if
it was a property, but with the difference that you prepend it with a
dollar sign "$". You are free to choose any name
for your variables as long as you only use characters and
numbers.
There are two different usages in the above code: the variable
$hello contains a Text
object which was copied from the object path
boilerplates.helloMessage. The second variable
$goodbye is not an object, but contains a literal
string. Both are combined into a new string for the
myObject.value property. That means: you can assign
objects just as well as literals or numbers to your variables and
concatenate them to a string. The rule however is that objects must be
TypoScript Content Objects which can be rendered
as content (contrary to simple TypoScript Objects which are mainly
used for storing configuration).
The scope of variables is restricted to the TypoScript object they have been defined in. The following code illustrates how variables can be accessed – and how not:
Example 3.22. The scope of variables
someObjects {
firstObject = Text
firstObject.$message = "Hey, I\'m the first object."
firstObject.value = $message
secondObject = Text
# This doesn't work because $message is out of scope:
secondObject.value = $message
# This doesn't work either:
secondObject.value = firstObject.$message
# But this is a way to get the message of the firstObject:
secondObject.$secondMessage < someObjects.firstObject
secondObject.value = $secondMessage
# However, this code does not lead to the expected result:
secondObject.$secondMessage < someObjects.firstObject.value
secondObject.value = $secondMessage
# Variables can be used within single- and double quotes,
# but the dollar sign always has to be escaped:
thirdObject = Text
thirdObject.$product = "Mountainbike"
thirdObject.$price = "1500"
thirdObject.value = "You just bought a $product for \$ $price."
thirdObject.value = 'You just bought a $product for \$ $price."
}
The object types which were used in the previous examples exist
as real PHP classes, therefore TypoScripts maps TypoScript objects to
real PHP objects which have the same properties like the TypoScript
objects. The names of these object types, for example
Text or HTML are mapped
to class names (more precisely: component names). The Typoscript
Object called Text is represented by a class
with the full name
F3_CMS_TypoScript_Text.
The reason why this mapping works is that a default
namespace has been defined for the TypoScript object types.
In the TYPO3 CMS the default namespace is
F3_CMS_TypoScript but it is possible to define
additional namespaces in order to use TypoScript object types which
are not available in the CMS package. It is even
possible to define a different default namespace.
Consider a package CoolForms which provides a
new kind of TypoScript object implemented by a class called
F3_CoolForms_TypoScript_Form. The following
TypoScript code would use that new object type by declaring a new
namespace:
Example 3.23. Using namespaces
namespace: forms = F3_CoolForms_TypoScript lib.someTextObject = Text lib.someTextObject.value = "Hello, this is a regular Text object." lib.someFormObject = forms:Form lib.someFormObject.fields.1 = forms:InputField lib.someFormObject.fields.1.label = "Your name"
You can also add include instructions to your TypoScript code to externalize certain parts or the whole TypoScript code. Here is an example for an include instruction:
We'll have to discuss how a TypoScript include has to look like as soon as the concept for the Resource Manager is settled.