Login / Status
developer.Resource
Home . Documentation . Document Library . Core Documentation
Sponsors
hosted by punkt.deTYPO3 and Open Source Magazine

1.6. Coding

General PHP practices

  1. Always check by is_array() and reset() an array before introducing it in a while(list()=each()) loop. "foreach()" loops are prefered though and doesn't need reset() first.

  2. In comparing strings, use strcmp() (returns false if strings matches!), prefix supposed string variables with '(string)' or use “===” if both values in the comparison should also have the same type. Reason: Consider this: $id==”my_string”. This will return true if 1) $id is a string, “my_string” OR if 2) $id is an integer of zero in which case “my_string” is converted to an integer - which will be zero! So instead do one of these: '(string)$id==”my_string”' or '!strcmp($id,”my_string”)'. The same with switch-constructs: 'switch((string)$id) {....'

  3. When including files, either include them with an absolute reference (by prefixing some constant like PATH_site, PATH_typo3 and more) or prefix them with “current dir” (that is “./”). Example of relative inclusion: include(“./some_dir/some_file.php”); Notice that this rule may not yield an error if not adhered to. The errors if you leave out the “current dir” prefix will show up on systems with the php value “include_path” set to a value that does not include current dir. But most php-installs does.

  4. UNSOLVED regex issues:On some systems using another regex library curly braces {} in regexes will make trouble. However it has not yet been discovered how to handle this is a cross-library way. Case: “\{([0-9]*)\}” - here the curly braces are escaped with backslashes. Is that correct to do OR should they NOT be backslashes (which was a recent conclusion in class.t3lib_tsparser_ext.php 230502)According to sources, the problem occurs if PHP is compiled with '--with-regex=system' where '--with-pcre-regex=/usr' (or no setting presumably) does NOT cause the trouble.

  5. How to use array_merge():The php-function array_merge() will not override integer-keys in arrays. Integer keys will be renumbered as the function pleases. If you need to merge two arrays where keys - both integer and string - in the second array will override similar positions in the first array, please use t3lib_div::array_merge(). For multidimensional merging use t3lib_div::array_merge_recursive_overrule()

  6. Special care about file-functions, is_file, is_dir, is_writeable, file_exists!These functions may result in a warning like “Warning: stat failed in line ...” if they are used on a file path which does not exist. This behavior is detected for PHP 4.0.7+ and ironically these functions are in fact used to detect whether or not a certain path is valid! The solution is to prepend the function call with @, so with these functions, please prepend the function name like this example: @is_file(), @is_dir(), @file_exists(), etc.

  7. With fopen - always add the "b" parameter to files are binary safe

  8. is_executable() - use only when the server is NOT windows.

  9. For “safe_mode” / “open_basedir” compliance:

    1. mkdir(): do not use mkdir() with a directory name ending on a “/” - no slash in the end! (safe_mode)

    2. Temporary filenames: To get a temporary filename, do not use temporary filenames outside the system PATH_site. Rather use:

      1. tempnam(PATH_site.'typo3temp/','');

      2. or better, use the TYPO3 API function: t3lib_div::tempnam($filePrefix)

      You will get problems with “open_basedir” configurations if temporary files are created in “/tmp/”REMEMBER to unlink temporary files afterwards. Can be done by t3lib_div::unlink_tempfile()

    3. Uploading files: When you upload files do not check them by for example “is_file()” or “filesize()” but rather “is_uploaded_file()”  for checking. Read filesize from HTTP_POST_FILES.The general rule is; you cannot access the uploaded file without moving it first. So you should a) move the uploaded file to a temp-file, then read it, then delete the temp file.Moving uploaded files can easily be done by t3lib_div::upload_to_tempfile(). Just remember to remove the temporary file after operations with t3lib_div::unlink_tempfile()

  10. Output variables to browser: Output values through htmlspecialchars()

  11. Build dynamic SQL: Insert values in SQL queries through either addslashes()/$GLOBALS['TYPO3_DB']->quoteStr() or intval() (depending of value of course) and insert values in quotes.

TYPO3 specific practices

  1. Always call t3lib_div::loadTCA($tablename) before you use any other parts of the $TCA array than the [ctrl]-section (loads the table should it happen to be dynamically configured.

  2. Always get system/PHP variables (such as REQUEST_URI or REMOTE_ADDR or SCRIPT_FILENAME) by t3lib_div::getIndpEnv(). Avoid getenv(). See table/arguments above.

  3. Always make a new object instance using t3lib_div::makeInstance($className)

  4. Always format content for a <textarea>-field with t3lib_div::formatForTextarea()

  5. Backend: Try to use the functions $TBE_TEMPLATE->formWidth() [input fields] and $TBE_TEMPLATE->formWidthText() [textarea] to get the proper cols/rows/size/style attributes for fields. This not only secures a homogenious display across browsers, but it also makes sure that wrapping/no-wrapping will occur correctly across browsers as well! (which is really a plague to make happen correctly...)

  6. Using GD functions imageTTFBBox and imageTTFtext you should pass the font-size parameter through t3lib_div::freetypeDpiComp() - this will compensate the size if FreeType2 is used (and configured for in TYPO3_CONF_VARS)

  7. Don't expect 'index.php' to be a default document; Always link to “../index.php?param=...” instead of “../?param=...” (which may not work for some configurations)

  8. http/https handling: When detecting absolute urls either use parse_url() and look for the “scheme” key in the array or make a 'substr($str,0,4)==”http”' - dont test for 'http://' unless you are certain you want to fail “https://” connections!If you need to know the current scheme, subtract if from TYPO3_REQUEST_HOST or another var from t3lib_div::getIndpEnv()If you need to prefix a scheme to an “un-schemed” url (eg. 'www.typo3.com') use “http://” (this is the most common anyways).

  9. SQL: Use general field types in MySQL. Avoid all kinds of time/date fields which are specific to MySQL. For timestamps always use an integer and insert “UNIX-time”. It's not humanreadable, but it will be portable the day we implement Database Abstraction.

  10. When values/labels from the database get inserted into HTML tag attribute values or JavaScripts then they have to get quoted properly. This can be done using "t3lib_div::quoteJSvalue($value, $inScriptTags)". Set the second parameter to true when the value get's used in <script> tags and to false if it get's used in an attribute of a tag (onClick i.e) [- Bernhard Kraft]

Cross platform issues (Windows/Unix)

  1. Paths in TYPO3 are always with forward single slashes like this:

    Relative (win/unix)path/to/a/file.phpAbsolute (unix)/my/path/to/a/file.php Absolute (win)C:/my/path/to/a/file.php (Exceptions include paths to external programs like ImageMagick (single-backslash) and uploaded files which comes with single-backslash and should NOT be changed before processing. But all internal files should follow the scheme above)

    1. Check absolute paths with t3lib_div::isAbsPath($path) - this will test both Unix (“/”) and Windows (“x:/”) absolute paths.

    2. If you (for some reason) need to convert a windows path with backslashes (sometimes double-backslashes) you can pass it to t3lib_div::fixWindowsFilePath() which will return it with single forward slashes. The function does not harm paths without backslashes, so you can use it in general.

    3. Don't use the “:” (colon) as a explode-token if there is any chance a component might be a path - in that case it breaks on windows!

  2. Uploading filesUploading files may be tricky for some reasons: On Windows the paths are WITH backslashes (not forward slashes) and those must not be converted before processing. And then in safe_mode you MUST use move_uploaded_file() to move it from the temp-dir. Read these notes for how to deal with this:

    1. Note that uploaded files on windows comes in the HTTP_POST_FILES array with backslashes! You should NOT convert this path, but let the system handle the file with the name given in HTTP_POST_FILES. However if you need to evaluate the path (eg. check for “/” slash in it...) use t3lib_div::fixWindowsFilePath() (see above. Searching for places where such a check is done may be successfull with this regex: strstr[[:space:]]*\([^,]*,"\/"\))

    2. If you pass an uploaded file as a resource to tcemain-class you should pass the original path from HTTP_POST_FILES of course. But it's VERY important (for windows paths with backslashes) that the handling of slashes is correct. You may need to set '->stripslashes_values=0;' so that tcemain does not strip the backslashes of the filenames if you call tcemain directly from your scripts! But then all ordinary data must also be without slashes. Alternatively you should pass the resource-list through addslashes() function before passing on to tcemain.

    3. For copying/moving (possibly uploaded) files, you should use t3lib_div::upload_copy_move($source,$destination). This will check if the file is uploaded and if so, use the move_uploaded_file() function (works with open_base_dir/safe_mode). Otherwise it's copied plainly with the copy() function.

    Also, see comments above about safe_mode and open_basedir compliance.

  3. Header(“Location: ”):(Find occurencies by 'header[[:space:]]*\(["']location:' regex)On some server setups it has been discovered that a header-location send to a script in another directory of the server does not inform the web browser about the new location. For instance if we send a 'header(“Location: /typo3/mod/my_module/index.php”)' from the '/typo3/alt_doc.php' then the script '/typo3/mod/my_module/index.php' is correctly informed about URL etc. but the browser is not! So if you try to make a relative reference, say,  back to the alt_doc.php script like '../../alt_doc.php' it will fail, because the web browser still thinks we are at the location '/typo3/'. The solution is to prefix all header-location URLS with “http://”. This is even a requirement in RFC 2068, section “14.30 Location”

    1. Always send URLS for Header(“Location: ”) through this function, t3lib_div::locationHeaderUrl($url). This will prefix “http://” if needed. $url's with “/” as first character (relative to host) and $url's with no scheme (that is no “http://” part, regarded relative to current directory) will be prefixed accordingly to become absolute URL's.Apparently header-locations to scripts in the same folder is not affected by this behavior (because they are in the same path...) but although you may not see the immediate impact (because the script is in the same directory or more likely you are on UNIX servers) you should use the function as a rule of thumb. That is the current policy.

PHP settings compliance

When you write your code, conform to the php.ini-recommended settings (see inside the “php.ini” files, there normally is a comment in the header about this!)

; - allow_call_time_pass_reference = Off
;     It's not possible to decide to force a variable to be passed by reference
;     when calling a function.  The PHP 4 style to do this is by making the
;     function require the relevant argument by reference.
; - register_globals = Off
;     Global variables are no longer registered for input data (POST, GET, cookies,
;     environment and other server variables).  Instead of using $foo, you must use
;     $HTTP_POST_VARS["foo"], $HTTP_GET_VARS["foo"], $HTTP_COOKIE_VARS["foo"],
;     $HTTP_ENV_VARS["foo"] or $HTTP_SERVER_VARS["foo"], depending on which kind
;     of input source you're expecting 'foo' to come from.
; - register_argc_argv = Off
;     Disables registration of the somewhat redundant $argv and $argc global
;     variables.
; - magic_quotes_gpc = Off
;     Input data is no longer escaped with slashes so that it can be sent into
;     SQL databases without further manipulation.  Instead, you should use the
;     function addslashes() on each input element you wish to send to a database.
; - variables_order = "GPCS"
;     The environment variables are not hashed into the $HTTP_ENV_VARS[].  To access
;     environment variables, you can use getenv() instead.

Further, you should program in compliance with these PHP settings:

  1. safe_mode = On

  2. safe_mode_gid = Off

  3. safe_mode_exec_dir = [path]

  4. open_basedir = [path]

  5. short_open_tags = Off

About escaped values in _GET and _POST

Notice that TYPO3 currently converts the HTTP_POST_VARS and HTTP_GET_VARS arrays to being escaped (or “slashed” if you like) if magic_quotes_gpc is “off”. So no matter what you do, expect these arrays to be slashed.

This is kind of backwards but has historical reasons; TYPO3 was simply started out at the time when PHP by default escaped all incoming input. The bad decision was made then to solve settings with unescaped values to force the values into being escaped. The result is: The values are consistently escaped (which is good) but they should rather have been consistently un-escaped (which is too late to change).

Despite the fact that we have a consistent situation it is strongly advised that you use the API functions t3lib_div::_GET(), t3lib_div::_POST() and t3lib_div::_GP() for accessing GET/POST content! These functions will return the values unescaped which is the state that input values should always be processed in (and strings should be escaped again with $GLOBALS['TYPO3_DB']->quoteStr() right before going into SQL queries).

Error code standard

There is no official position on this but Dan Frost has suggested a practice like this (which you can follow if you like):

Basically, every error message an extension / core thing throws 
(e.g. a debug or die) has a code of the form:[your initials][Date][time]You just write this as you're writing the code. E.g. if I was writing some code now, the error code would be:df200412240824Which I might use like:if($mustBeTrue) {        // do stuff} else {   die('Something bad happened df200412240824');}Then, when i see this i just grep for "df200412240824". The alternative could be "some error messgage".__LINE__.__FILE__; 

The date/time format should follow ISO 8601; [year][month][day][hour-24][minute] (suggested by Andreas Otto)

Suggestions can be discussed on the developer mailing list.

References

Here are a few other references to coding guidelines:

  1. http://www.whip3.net/whitepapers/phpguide.php

    This guideline is generally giving good practices to follow, but at any point where it is incompatible with this document, this documents guideline will take precedence (That is the case when talking about curly braces of functions and classes for instance).

  2. http://pear.php.net/manual/en/standards.php (PEAR guidelines)

    These guidelines are the “official” coding guidelines for PHP code - and in particular for that in the PEAR repository. Generally we agree with these guidelines except on the following points:

    1. We recommend using tabs for indentation - not spaces

    2. We do not use “one-true-brace” principle for functions/methods. This is important for the Extension Development Evaluators ability to parse the function/method comments!

    3. The header comment looks different in the formatting.

    4. Classes are named in studly caps.

    5. “break” in switch constructs is not indented.

    The arguments for these incompatibilities have been stated previously in this document.