.. _Cases:
Real Code Cases
===================
Introduction
---------------
All the examples in this section are real code, extracted from major PHP applications.
List of real code Cases
------------------------------
.. _case-$this-belongs-to-classes-or-traits:
$this Belongs To Classes Or Traits
##################################
.. _case-openemr-classes-thisisforclasses:
OpenEMR
+++++++
:ref:`$this-belongs-to-classes-or-traits`, in ccr/display.php:24.
$this is used to call the document_upload_download_log() method, although this piece of code is not part of a class, nor is included in a class.
.. code-block:: php
get_url();
$storagemethod = $d->get_storagemethod();
$couch_docid = $d->get_couch_docid();
$couch_revid = $d->get_couch_revid();
if ($couch_docid && $couch_revid) {
$couch = new CouchDB();
$data = array($GLOBALS['couchdb_dbase'],$couch_docid);
$resp = $couch->retrieve_doc($data);
$xml = base64_decode($resp->data);
if ($content=='' && $GLOBALS['couchdb_log']==1) {
$log_content = date('Y-m-d H:i:s')." ==> Retrieving document\r\n";
$log_content = date('Y-m-d H:i:s')." ==> URL: ".$url."\r\n";
$log_content .= date('Y-m-d H:i:s')." ==> CouchDB Document Id: ".$couch_docid."\r\n";
$log_content .= date('Y-m-d H:i:s')." ==> CouchDB Revision Id: ".$couch_revid."\r\n";
$log_content .= date('Y-m-d H:i:s')." ==> Failed to fetch document content from CouchDB.\r\n";
//$log_content .= date('Y-m-d H:i:s')." ==> Will try to download file from HardDisk if exists.\r\n\r\n";
$this->document_upload_download_log($d->get_foreign_id(), $log_content);
die(xlt("File retrieval from CouchDB failed"));
}
.. _case-**-for-exponent:
** For Exponent
###############
.. _case-traq-php-newexponent:
Traq
++++
:ref:`**-for-exponent`, in src/views/layouts/_footer.phtm:5.
pow(1024, 2) could be (1023 ** 2), to convert bytes into Mb.
.. code-block:: php
=round((microtime(true) - START_TIME), 2); ?>s, mb
.. _case-teampass-php-newexponent:
TeamPass
++++++++
:ref:`**-for-exponent`, in includes/libraries/Authentication/phpseclib/Math/BigInteger.php:286.
pow(2, 62) could also be hard coded with 0x4000000000000000.
.. code-block:: php
pow(2, 62)
.. _case-@-operator:
@ Operator
##########
.. _case-phinx-structures-noscream:
Phinx
+++++
:ref:`@-operator`, in src/Phinx/Util/Util.php:239.
fopen() may be tested for existence, readability before using it. Although, it actually emits some errors on Windows, with network volumes.
.. code-block:: php
$isReadable = @\fopen($filePath, 'r') !== false;
if (!$filePath || !$isReadable) {
throw new \Exception(sprintf(Cannot open file %s \n, $filename));
}
.. _case-phpipam-structures-noscream:
PhpIPAM
+++++++
:ref:`@-operator`, in functions/classes/class.Log.php:322.
Variable and index existence should always be tested with isset() : it is faster than using ``@``.
.. code-block:: php
$_SESSION['ipamusername']
.. _case-abstract-or-implements:
Abstract Or Implements
######################
.. _case-zurmo-classes-abstractorimplements:
Zurmo
+++++
:ref:`abstract-or-implements`, in app/protected/extensions/zurmoinc/framework/views/MassEditProgressView.php:30.
The class MassEditProgressView extends ProgressView, which is an abstract class. That class defines one abstract method : abstract protected function headerLabelPrefixContent(). Yet, the class MassEditProgressView doesn't implements this method. This means that the class can't be instatiated, and indeed, it isn't. The class MassEditProgressView is subclassed, by the class MarketingListMembersMassSubscribeProgressView, which implements the method headerLabelPrefixContent(). As such, MassEditProgressView should be marked abstract, so as to prevent any instantiation attempt.
.. code-block:: php
class MassEditProgressView extends ProgressView {
/**/
}
.. _case-add-default-value:
Add Default Value
#################
.. _case-zurmo-functions-adddefaultvalue:
Zurmo
+++++
:ref:`add-default-value`, in wp-admin/includes/misc.php:74.
Default values may be a literal (1, 'abc', ...), or a constant : global or class. Here, MissionsListConfigurationForm::LIST_TYPE_AVAILABLE may be used directly in the signature of the method
.. code-block:: php
public function getMetadataFilteredByOption($option)
{
if ($option == null)
{
$option = MissionsListConfigurationForm::LIST_TYPE_AVAILABLE;
}
.. _case-typo3-functions-adddefaultvalue:
Typo3
+++++
:ref:`add-default-value`, in typo3/sysext/indexed_search/Classes/FileContentParser.php:821.
$extension could get a default value to handle default situations : for example, a file is htm format by default, unless better known. Also, the if/then structure could get a 'else' clause, to handle unknown situations : those are situations where the extension is provided but not known, in particular when the icon is missing in the storage folder.
.. code-block:: php
public function getIcon($extension)
{
if ($extension === 'htm') {
$extension = 'html';
} elseif ($extension === 'jpeg') {
$extension = 'jpg';
}
return 'EXT:indexed_search/Resources/Public/Icons/FileTypes/' . $extension . '.gif';
}
.. _case-adding-zero:
Adding Zero
###########
.. _case-thelia-structures-addzero:
Thelia
++++++
:ref:`adding-zero`, in core/lib/Thelia/Model/Map/ProfileResourceTableMap.php:250.
This return statement is doing quite a lot, including a buried '0 + $offset'. This call is probably an echo to '1 + $offset', which is a little later in the expression.
.. code-block:: php
return serialize(array((string) $row[TableMap::TYPE_NUM == $indexType ? 0 + $offset : static::translateFieldName('ProfileId', TableMap::TYPE_PHPNAME, $indexType)], (string) $row[TableMap::TYPE_NUM == $indexType ? 1 + $offset : static::translateFieldName('ResourceId', TableMap::TYPE_PHPNAME, $indexType)]));
.. _case-openemr-structures-addzero:
OpenEMR
+++++++
:ref:`adding-zero`, in interface/forms/fee_sheet/new.php:466:534.
$main_provid is filtered as an integer. $main_supid is then filtered twice : one with the sufficent (int) and then, added with 0.
.. code-block:: php
if (!$alertmsg && ($_POST['bn_save'] || $_POST['bn_save_close'] || $_POST['bn_save_stay'])) {
$main_provid = 0 + $_POST['ProviderID'];
$main_supid = 0 + (int)$_POST['SupervisorID'];
//.....
.. _case-already-parents-interface:
Already Parents Interface
#########################
.. _case-wordpress-interfaces-alreadyparentsinterface:
WordPress
+++++++++
:ref:`already-parents-interface`, in src/Phinx/Db/Adapter/AbstractAdapter.php:41.
SqlServerAdapter extends PdoAdapter, PdoAdapter extends AbstractAdapter. The first and the last both implements AdapterInterface. Only one is needed.
.. code-block:: php
/**
* Base Abstract Database Adapter.
*/
abstract class AbstractAdapter implements AdapterInterface
{
/// In the src/src/Phinx/Db/Adapter/SqlServerAdapter.php, line 45
/**
* Phinx SqlServer Adapter.
*
*/
class SqlServerAdapter extends PdoAdapter implements AdapterInterface
{
.. _case-thelia-interfaces-alreadyparentsinterface:
Thelia
++++++
:ref:`already-parents-interface`, in core/lib/Thelia/Core/Template/Loop/BaseSpecificModule.php:35.
PropelSearchLoopInterface is implemented by both BaseSpecificModule and Payment
.. code-block:: php
abstract class BaseSpecificModule extends BaseI18nLoop implements PropelSearchLoopInterface
/* in file core/lib/Thelia/Core/Template/Loop/Payment.php, line 28 */
class Payment extends BaseSpecificModule implements PropelSearchLoopInterface
.. _case-altering-foreach-without-reference:
Altering Foreach Without Reference
##################################
.. _case-contao-structures-alteringforeachwithoutreference:
Contao
++++++
:ref:`altering-foreach-without-reference`, in core-bundle/src/Resources/contao/classes/Theme.php:613.
$tmp[$kk] is &$vv.
.. code-block:: php
foreach ($tmp as $kk=>$vv)
{
// Do not use the FilesModel here – tables are locked!
$objFile = $this->Database->prepare("SELECT uuid FROM tl_files WHERE path=?")
->limit(1)
->execute($this->customizeUploadPath($vv));
$tmp[$kk] = $objFile->uuid;
}
.. _case-wordpress-structures-alteringforeachwithoutreference:
WordPress
+++++++++
:ref:`altering-foreach-without-reference`, in wp-admin/includes/misc.php:74.
$ids[$index] is &$rrid.
.. code-block:: php
foreach($ids as $index => $rrid)
{
if($rrid == $this->Id)
{
$ids[$index] = $_id;
$write = true;
break;
}
}
.. _case-always-positive-comparison:
Always Positive Comparison
##########################
.. _case-magento-structures-nevernegative:
Magento
+++++++
:ref:`always-positive-comparison`, in app/code/core/Mage/Dataflow/Model/Profile.php:85.
strlen(($actiosXML) will never be negative, and hence, is always false. This exception is never thrown.
.. code-block:: php
if (strlen($actionsXML) < 0 &&
@simplexml_load_string('' . $actionsXML . '', null, LIBXML_NOERROR) === false) {
Mage::throwException(Mage::helper('dataflow')->__(Actions XML is not valid.));
}
.. _case-ambiguous-array-index:
Ambiguous Array Index
#####################
.. _case-prestashop-arrays-ambiguouskeys:
PrestaShop
++++++++++
:ref:`ambiguous-array-index`, in src/PrestaShopBundle/Install/Install.php:532.
Null, as a key, is actually the empty string.
.. code-block:: php
$list = array(
'products' => _PS_PROD_IMG_DIR_,
'categories' => _PS_CAT_IMG_DIR_,
'manufacturers' => _PS_MANU_IMG_DIR_,
'suppliers' => _PS_SUPP_IMG_DIR_,
'stores' => _PS_STORE_IMG_DIR_,
null => _PS_IMG_DIR_.'l/', // Little trick to copy images in img/l/ path with all types
);
.. _case-mautic-arrays-ambiguouskeys:
Mautic
++++++
:ref:`ambiguous-array-index`, in app/bundles/CoreBundle/Entity/CommonRepository.php:314.
True is turned into 1 (integer), and false is turned into 0 (integer).
.. code-block:: php
foreach ($metadata->getAssociationMappings() as $field => $association) {
if (in_array($association['type'], [ClassMetadataInfo::ONE_TO_ONE, ClassMetadataInfo::MANY_TO_ONE])) {
$baseCols[true][$entityClass][] = $association['joinColumns'][0]['name'];
$baseCols[false][$entityClass][] = $field;
}
}
.. _case-ambiguous-visibilities:
Ambiguous Visibilities
######################
.. _case-typo3-classes-ambiguousvisibilities:
Typo3
+++++
:ref:`ambiguous-visibilities`, in typo3/sysext/backend/Classes/Controller/NewRecordController.php:90.
$allowedNewTables is declared once protected and once public. $allowedNewTables is rare : 2 occurences. This may lead to confusion about access to this property.
.. code-block:: php
class NewRecordController
{
/.. many lines../
/**
* @var array
*/
protected $allowedNewTables;
class DatabaseRecordList
{
/..../
/**
* Used to indicate which tables (values in the array) that can have a
* create-new-record link. If the array is empty, all tables are allowed.
*
* @var string[]
*/
public $allowedNewTables = [];
.. _case-argument-should-be-typehinted:
Argument Should Be Typehinted
#############################
.. _case-dolphin-functions-shouldbetypehinted:
Dolphin
+++++++
:ref:`argument-should-be-typehinted`, in Dolphin-v.7.3.5/plugins/intervention-image/Intervention/Image/Gd/Commands/WidenCommand.php:20.
This closures make immediate use of the $constraint argument, and calls its method aspectRatio. No check is made on this argument, and it may easily be mistaken with another class, or a null. Adding a typehint here will ensure a more verbose development error and help detect misuse of the closure.
.. code-block:: php
$this->arguments[2] = function ($constraint) use ($additionalConstraints) {
$constraint->aspectRatio();
if(is_callable($additionalConstraints))
$additionalConstraints($constraint);
};
.. _case-mautic-functions-shouldbetypehinted:
Mautic
++++++
:ref:`argument-should-be-typehinted`, in app/bundles/PluginBundle/Helper/IntegrationHelper.php:374.
This piece of code inside a 275 lines method. Besides, there are 11 classes that offer a 'getPriority' method, although $returnServices could help to semantically reduce the number of possible classes. Here, typehints on $a and $b help using the wrong kind of object.
.. code-block:: php
if (empty($alphabetical)) {
// Sort by priority
uasort($returnServices, function ($a, $b) {
$aP = (int) $a->getPriority();
$bP = (int) $b->getPriority();
if ($aP === $bP) {
return 0;
}
return ($aP < $bP) ? -1 : 1;
});
.. _case-assign-and-lettered-logical-operator-precedence:
Assign And Lettered Logical Operator Precedence
###############################################
.. _case-xataface-php-assignand:
xataface
++++++++
:ref:`assign-and-lettered-logical-operator-precedence`, in Dataface/LanguageTool.php:265.
The usage of 'and' here is a workaround for PHP version that have no support for the coalesce. $autosubmit receives the value of $params['autosubmit'] only if the latter is set. Yet, with = having higher precedence over 'and', $autosubmit is mistaken with the existence of $params['autosubmit'] : its value is actually omitted.
.. code-block:: php
$autosubmit = isset($params['autosubmit']) and $params['autosubmit'];
.. _case-assign-default-to-properties:
Assign Default To Properties
############################
.. _case-livezilla-classes-makedefault:
LiveZilla
+++++++++
:ref:`assign-default-to-properties`, in livezilla/_lib/functions.external.inc.php:174.
Flags may default to array() in the class definition. Filled array(), with keys and values, are also possible.
.. code-block:: php
class OverlayChat
{
public $Botmode;
public $Human;
public $HumanGeneral;
public $RepollRequired;
public $OperatorCount;
public $Flags;
public $LastMessageReceived;
public $LastPostReceived;
public $IsHumanChatAvailable;
public $IsChatAvailable;
public $ChatHTML;
public $OverlayHTML;
public $PostHTML;
public $FullLoad;
public $LanguageRequired = false;
public $LastPoster;
public $EyeCatcher;
public $GroupBuilder;
public $CurrentOperatorId;
public $BotTitle;
public $OperatorPostCount;
public $PlaySound;
public $SpeakingToHTML;
public $SpeakingToAdded;
public $Version = 1;
public static $MaxPosts = 50;
public static $Response;
function __construct()
{
$this->Flags = array();
VisitorChat::$Router = new ChatRouter();
}
.. _case-phpmyadmin-classes-makedefault:
phpMyAdmin
++++++++++
:ref:`assign-default-to-properties`, in libraries/classes/Console.ph:55.
_isEnabled may default to true. It could also default to a class constant.
.. code-block:: php
class Console
{
/**
* Whether to display anything
*
* @access private
* @var bool
*/
private $_isEnabled;
// some code ignored here
/**
* Creates a new class instance
*/
public function __construct()
{
$this->_isEnabled = true;
.. _case-avoid-concat-in-loop:
Avoid Concat In Loop
####################
.. _case-suitecrm-performances-noconcatinloop:
SuiteCrm
++++++++
:ref:`avoid-concat-in-loop`, in include/export_utils.php:433.
$line is build in several steps, then then final version is added to $content. It would be much faster to make $content an array, and implode it once after the loop.
.. code-block:: php
foreach($records as $record)
{
$line = implode("\\ . getDelimiter() . "\"", $record);
$line = "\"" . $line;
$line .= ""\r\n";
$line = parseRelateFields($line, $record, $customRelateFields);
$content .= $line;
}
.. _case-thinkphp-performances-noconcatinloop:
ThinkPHP
++++++++
:ref:`avoid-concat-in-loop`, in ThinkPHP/Common/functions.php:720.
The foreach loop appends the $name and builds a fully qualified name.
.. code-block:: php
if (!C('APP_USE_NAMESPACE')) {
$class = parse_name($name, 1);
import($module . '/' . $layer . '/' . $class . $layer);
} else {
$class = $module . '\' . $layer;
foreach ($array as $name) {
$class .= '\' . parse_name($name, 1);
}
// 导入资源类库
if ($extend) {
// 扩展资源
$class = $extend . '\' . $class;
}
}
return $class . $layer;
.. _case-avoid-optional-properties:
Avoid Optional Properties
#########################
.. _case-churchcrm-classes-avoidoptionalproperties:
ChurchCRM
+++++++++
:ref:`avoid-optional-properties`, in src/ChurchCRM/BackupManager.php:401.
Backuptype is initialized with null, and yet, it isn't checked for any invalid valid values, in particular in switch() structures.
.. code-block:: php
// BackupType is initialized with null
class JobBase
{
/**
*
* @var BackupType
*/
protected $BackupType;
// In the child class BackupJob, BackupType may be of any type
class BackupJob extends JobBase
{
/**
*
* @param String $BaseName
* @param BackupType $BackupType
* @param Boolean $IncludeExtraneousFiles
*/
public function __construct($BaseName, $BackupType, $IncludeExtraneousFiles, $EncryptBackup, $BackupPassword)
{
$this->BackupType = $BackupType;
// Later, Backtype is not checked with all values :
try {
$this->DecryptBackup();
switch ($this->BackupType) {
case BackupType::SQL:
$this->RestoreSQLBackup($this->RestoreFile);
break;
case BackupType::GZSQL:
$this->RestoreGZSQL();
break;
case BackupType::FullBackup:
$this->RestoreFullBackup();
break;
// Note : no default case here
}
.. _case-dolibarr-classes-avoidoptionalproperties:
Dolibarr
++++++++
:ref:`avoid-optional-properties`, in htdocs/product/stock/class/productlot.class.php:149.
$this->fk_product is tested for value 11 times while being used in this class. All detected situations were checking the presence of the property before usage.
.. code-block:: php
class Productlot extends CommonObject
{
// more code
/**
* @var int ID
*/
public $fk_product;
// Checked usage of fk_product
// line 341
$sql .= ' fk_product = '.(isset($this->fk_product) ? $this->fk_product : "null").',';
.. _case-avoid-substr()-one:
Avoid Substr() One
##################
.. _case-churchcrm-structures-nosubstrone:
ChurchCRM
+++++++++
:ref:`avoid-substr()-one`, in src/Login.php:141.
No need to call substr() to get only one char.
.. code-block:: php
if (substr($LocationFromGet, 0, 1) == /) {
$LocationFromGet = substr($LocationFromGet, 1);
}
.. _case-livezilla-structures-nosubstrone:
LiveZilla
+++++++++
:ref:`avoid-substr()-one`, in livezilla/_lib/objects.global.inc.php:2243.
No need to call substr() to get only one char.
.. code-block:: php
$_hex = str_replace(#, , $_hex);
if(strlen($_hex) == 3) {
$r = hexdec(substr($_hex,0,1).substr($_hex,0,1));
$g = hexdec(substr($_hex,1,1).substr($_hex,1,1));
$b = hexdec(substr($_hex,2,1).substr($_hex,2,1));
} else {
$r = hexdec(substr($_hex,0,2));
$g = hexdec(substr($_hex,2,2));
$b = hexdec(substr($_hex,4,2));
}
$rgb = array($r, $g, $b);
return $rgb;
.. _case-avoid-glob()-usage:
Avoid glob() Usage
##################
.. _case-phinx-performances-noglob:
Phinx
+++++
:ref:`avoid-glob()-usage`, in src/Phinx/Migration/Manager.php:362.
glob() searches for a list of files in the migration folder. Those files are not known, but they have a format, as checked later with the regex : a combinaison of ``FilesystemIterator`` and ``RegexIterator`` would do the trick too.
.. code-block:: php
$phpFiles = glob($config->getMigrationPath() . DIRECTORY_SEPARATOR . '*.php');
// filter the files to only get the ones that match our naming scheme
$fileNames = array();
/** @var AbstractMigration[] $versions */
$versions = array();
foreach ($phpFiles as $filePath) {
if (preg_match('/([0-9]+)_([_a-z0-9]*).php/', basename($filePath))) {
.. _case-nextcloud-performances-noglob:
NextCloud
+++++++++
:ref:`avoid-glob()-usage`, in lib/private/legacy/helper.php:185.
Recursive copy of folders, based on scandir(). ``DirectoryIterator`` and ``FilesystemIterator`` would do the same without the recursion.
.. code-block:: php
static function copyr($src, $dest) {
if (is_dir($src)) {
if (!is_dir($dest)) {
mkdir($dest);
}
$files = scandir($src);
foreach ($files as $file) {
if ($file != . && $file != ..) {
self::copyr($src/$file, $dest/$file);
}
}
} elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
copy($src, $dest);
}
}
.. _case-avoid-set\_error\_handler-$context-argument:
Avoid set_error_handler $context Argument
#########################################
.. _case-shopware-php-avoidseterrorhandlercontextarg:
shopware
++++++++
:ref:`avoid-set\_error\_handler-$context-argument`, in engine/Shopware/Plugins/Default/Core/ErrorHandler/Bootstrap.php:162.
The registered handler is a local method, called ``errorHandler``, which has 6 arguments, and relays those 6 arguments to set_error_handler().
.. code-block:: php
public function registerErrorHandler($errorLevel = E_ALL)
{
// Only register once. Avoids loop issues if it gets registered twice.
if (self::$_registeredErrorHandler) {
set_error_handler([$this, 'errorHandler'], $errorLevel);
return $this;
}
self::$_origErrorHandler = set_error_handler([$this, 'errorHandler'], $errorLevel);
self::$_registeredErrorHandler = true;
return $this;
}
.. _case-vanilla-php-avoidseterrorhandlercontextarg:
Vanilla
+++++++
:ref:`avoid-set\_error\_handler-$context-argument`, in library/core/functions.error.php:747.
Gdn_ErrorHandler is a function that requires 6 arguments.
.. code-block:: php
set_error_handler('Gdn_ErrorHandler', E_ALL & ~E_STRICT)
.. _case-bad-constants-names:
Bad Constants Names
###################
.. _case-prestashop-constants-badconstantnames:
PrestaShop
++++++++++
:ref:`bad-constants-names`, in src/PrestaShopBundle/Install/Upgrade.php:214.
INSTALL_PATH is a valid name for a constant. __PS_BASE_URI__ is not a valid name.
.. code-block:: php
require_once(INSTALL_PATH . 'install_version.php');
// needed for upgrade before 1.5
if (!defined('__PS_BASE_URI__')) {
define('__PS_BASE_URI__', str_replace('//', '/', '/'.trim(preg_replace('#/(install(-dev)?/upgrade)$#', '/', str_replace('\', '/', dirname($_SERVER['REQUEST_URI']))), '/').'/'));
}
.. _case-zencart-constants-badconstantnames:
Zencart
+++++++
:ref:`bad-constants-names`, in zc_install/ajaxTestDBConnection.php:10.
A case where PHP needs help : if the PHP version is older than 5.3, then it is valid to compensate. Though, this __DIR__ has a fixed value, wherever it is used, while the official __DIR__ change from dir to dir.
.. code-block:: php
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
.. _case-bail-out-early:
Bail Out Early
##############
.. _case-openemr-structures-bailoutearly:
OpenEMR
+++++++
:ref:`bail-out-early`, in interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Controller/EncounterccdadispatchController.php:69.
This is a typical example of a function mostly controlled by one condition. It could be rewrite as 'if($validResult != 'existingpatient')' then return. The 'else' clause is not used anymore, and the whole block of code is now the main sequence of the method.
.. code-block:: php
public function ccdaFetching($parameterArray = array())
{
$validResult = $this->getEncounterccdadispatchTable()->valid($parameterArray[0]);
// validate credentials
if ($validResult == 'existingpatient') {
/// Long bloc of code
} else {
return '