Completed
Push — master ( 41fafc...54e7a7 )
by Andrii
07:29
created

HashConditionBuilder::build()   B

Complexity

Conditions 8
Paths 16

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 8.512

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 12
cts 15
cp 0.8
rs 8.4444
c 0
b 0
f 0
cc 8
nc 16
nop 2
crap 8.512
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\db\conditions;
9
10
use yii\db\ExpressionBuilderInterface;
11
use yii\db\ExpressionBuilderTrait;
12
use yii\db\ExpressionInterface;
13
use yii\db\Query;
14
use yii\helpers\ArrayHelper;
15
16
/**
17
 * Class HashConditionBuilder builds objects of [[HashCondition]]
18
 *
19
 * @author Dmytro Naumenko <[email protected]>
20
 * @since 2.0.14
21
 */
22
class HashConditionBuilder implements ExpressionBuilderInterface
23
{
24
    use ExpressionBuilderTrait;
25
26
27
    /**
28
     * Method builds the raw SQL from the $expression that will not be additionally
29
     * escaped or quoted.
30
     *
31
     * @param ExpressionInterface|HashCondition $expression the expression to be built.
32
     * @param array $params the binding parameters.
33
     * @return string the raw SQL that will not be additionally escaped or quoted.
34
     */
35 3
    public function build(ExpressionInterface $expression, array &$params = [])
36
    {
37 3
        $hash = $expression->getHash();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface yii\db\ExpressionInterface as the method getHash() does only exist in the following implementations of said interface: yii\db\conditions\HashCondition.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
38 3
        $parts = [];
39 3
        foreach ($hash as $column => $value) {
40 3
            if (ArrayHelper::isTraversable($value) || $value instanceof Query) {
41
                // IN condition
42
                $parts[] = $this->queryBuilder->buildCondition(new InCondition($column, 'IN', $value), $params);
43
            } else {
44 3
                if (strpos($column, '(') === false) {
45 3
                    $column = $this->queryBuilder->db->quoteColumnName($column);
46
                }
47 3
                if ($value === null) {
48
                    $parts[] = "$column IS NULL";
49 3
                } elseif ($value instanceof ExpressionInterface) {
50
                    $parts[] = "$column=" . $this->queryBuilder->buildExpression($value, $params);
51
                } else {
52 3
                    $phName = $this->queryBuilder->bindParam($value, $params);
53 3
                    $parts[] = "$column=$phName";
54
                }
55
            }
56
        }
57
58 3
        return count($parts) === 1 ? $parts[0] : '(' . implode(') AND (', $parts) . ')';
59
    }
60
}
61