Legacy Documentation
You are using the documentation for version 4.3.8. Go here for the latest version.
Performance Guidelines¶
- POSIX Extended Regular Expressions
- Perl-compatible (PCRE) Regular Expressions
- PCRE Subpattern Capture Optimization
- PCRE Backreference Caution
- Avoid Unnecessary String Replacements
- Use sprintf instead of str_replace
- Smart Substring Matching
- for() Loops
- Heredoc Strings
- One-time Use Variables
- Redundant Queries
POSIX Extended Regular Expressions¶
Do not use POSIX Extended regular expression functions. When you
need a regular expression function, always use the Perl-compatible
(PCRE) preg_
functions.
Note
NEVER USE: ereg()
, eregi()
, ereg_replace()
,
eregi_replace()
, split()
, spliti()
, or sql_regcase()
Perl-compatible (PCRE) Regular Expressions¶
Do not use regular expression functions unless you need to.
INCORRECT:
$str = preg_replace('/foo/', 'bar', $str);
$arr = preg_split('|', $str);
$arr = preg_split('|', $str, -1, PREG_SPLIT_NO_EMPTY); // $str is '1|2|3|4|'
CORRECT:
$str = str_replace('foo', 'bar', $str);
$arr = explode('|', $str);
$arr = explode('|', trim($str, '|')); // $str is '1|2|3|4|'
PCRE Subpattern Capture Optimization¶
Use ?:
at the start of a subpattern if it does not need to be
captured for faster execution and memory conservation.
INCORRECT:
$str = preg_replace('/xyz([0-9]+)/', 'zyx', $str); // not using the captured subpattern, so ?: should be used
CORRECT:
$str = preg_replace('/xyz(?:[0-9]+)/', 'zyx', $str); // correct use when not using the captured subpattern
$str = preg_replace('/xyz([0-9]+)/', 'zyx\\1', $str); // correct use of a captured subpattern
PCRE Backreference Caution¶
When using backreferences from a capture pattern combined with a parsed PHP variable, it is necessary to use dollar-sign backreferences to prevent it from looking for a backreference that doesn’t exist.
INCORRECT:
$foo = '123 Any Street';
$str = preg_replace('/(.*)/', "\\1{$foo}"); // expands to "\\1123 Any Street" and looks for backreference \\1123!
CORRECT:
$foo = '123 Any Street';
$str = preg_replace('/(.*)/', "${1}{$foo}");
Avoid Unnecessary String Replacements¶
Do not perform string replacements unless you need to, and know that the
search string exists in the subject string. Use strpos()
to see
if the replacement is necessary beforehand.
INCORRECT:
foreach ($items as $name => $value)
{
$str = str_replace($name, $value, $str);
}
CORRECT:
foreach ($items as $name => $value)
{
if (strpos($str, $name) !== FALSE)
{
$str = str_replace($name, $value, $str);
}
}
Remarkably, even if a match occurs on each loop, the additional
processing overhead for the strpos()
is negligible (on a 100kb
string it adds roughly 0.01 seconds for every 10,000 loops). If there
are loops that do not match, this method can approach 100% greater
efficiency.
Use sprintf instead of str_replace¶
When you need to add one or more variables to an existing string (e.g.
lang values), make sure to use sprintf
instead of str_replace
.
INCORRECT:
str_replace('%s', $channel, 'Currently editing the %s channel.')
CORRECT:
sprintf('Currently editing the %s channel.', $channel)
sprintf('%s is currently editing the %s channel.', $member_name, $channel)
Smart Substring Matching¶
When checking to see if a string has matching characters at the front of
the string only, use strncmp()
and strncasecmp()
instead of
substr()
. Especially on non-case sensitive checks, these functions
are much faster. Never use regular expression functions for this unless
you actually need a regular expression match.
INCORRECT:
if (substr($str, 0, 3) == 'foo')
if (substr(strtolower($str), 0, 3) == 'foo')
if (preg_match('/^foo/', $str)) // no need for regex match for this type of comparison
if (ereg('^foo', $str)) // AAAAAH! Never ever use ereg(), remember?
CORRECT:
if (strncmp($str, 'foo', 3) == 0)
if (strncasecmp($str, 'foo', 3) == 0)
strncmp() and
strncasecmp()
return < 0 if str1
is less than str2
, > 0 if str1
is greater
than str2
, and 0 if they are equal.
for() Loops¶
Do not perform calculations in the second expression of for()
loops,
as they will be executed on each iteration of the loop. Perform them
either in the first expression, or before entering the loop.
INCORRECT:
for ($i = 0; $i < count($arr); $i++)
CORRECT:
for ($i = 0, $foo = $count($arr); $i < $foo; $i++)
ALTERNATIVE:
$foo = count($arr); for ($i = 0; $i < $foo; $i++)
Heredoc Strings¶
Avoid heredoc strings unless absolutely necessary. They are more intensive for PHP to parse than single or double-quoted strings, resulting in slower code execution and increased memory usage.
One-time Use Variables¶
Avoid assigning new variables for one-time use. In the example below,
$foo
is never used again in the method.
INCORRECT:
$foo = 'a';
$str = $str.$foo;
CORRECT:
$str = $str.'a';
Redundant Queries¶
Avoid running queries in loops or running identical queries multiple times across page loads. Find a way to run such queries only once, outside of loops, by perhaps accessing all of the information your add- on will require for each iteration, storing it in a master array.
Make intelligent use of ee()->session->cache so these and other “meta” queries are executed only once no matter how many times a method is called on a page load.
Note
To keep the code example simple, the values in the $ids array below are assumed to have already been validated in the code prior to what is being shown. Do not neglect to validate and escape variables before using them in queries!
INCORRECT:
foreach ($ids as $id)
{
$query = ee()->db->query("SELECT name FROM exp_pre_email_addresses WHERE id = {$id}");
if ($query->num_rows() > 0)
{
$name = $query->row('name');
// rest of the code
}
}
CORRECT:
if ( ! isset(ee()->session->cache['super_class']['names']))
{
$query = ee()->db->query('SELECT id, name FROM exp_pre_email_addresses WHERE id IN ('.implode(',', $ids).')');
if ($query->num_rows() > 0)
{
foreach ($query->result_array() as $row)
{
ee()->session->cache['super_class']['names'][$row['id']] = $row['name'];
}
}
}
$names = ee()->session->cache['super_class']['names'];
// later in the code looped queries are no longer used
foreach ($ids as $id)
{
$name = $names[$id];
// rest of the code
}