Kohana_Jam_Behavior_Taxonomable   A
last analyzed

Complexity

Total Complexity 7

Size/Duplication

Total Lines 69
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 86.67%

Importance

Changes 0
Metric Value
wmc 7
lcom 1
cbo 5
dl 0
loc 69
ccs 26
cts 30
cp 0.8667
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A initialize() 0 14 1
B builder_call_with_terms() 0 40 6
1
<?php defined('SYSPATH') OR die('No direct script access.');
2
3
/**
4
 * I am a taxonamable behavior.
5
 * Attach me to models you want to have the `terms` association
6
 * and the `with_terms()` builder method.
7
 *
8
 * @author Haralan Dobrev <[email protected]>
9
 * @copyright 2014 Clippings Ltd.
10
 * @license http://spdx.org/licenses/BSD-3-Clause
11
 */
12
class Kohana_Jam_Behavior_Taxonomable extends Jam_Behavior {
13
14
    protected $_terms_association_name = 'terms';
15
16
    protected $_terms_association_options = array();
17
18
    protected $_terms_items_association_name = 'terms_items';
19
20
    protected $_terms_items_association_options = array(
21
        'as' => 'item',
22
        'foreign_model' => 'terms_item'
23
    );
24
25 1
    public function initialize(Jam_Meta $meta, $name)
26
    {
27 1
        parent::initialize($meta, $name);
28
29
        $meta
0 ignored issues
show
Bug introduced by
The method association does only exist in Kohana_Jam_Meta, but not in Jam_Association.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
30 1
            ->association(
31 1
                $this->_terms_association_name,
32 1
                Jam::association('taxonomy_terms', $this->_terms_association_options)
33
            )
34 1
            ->association(
35 1
                $this->_terms_items_association_name,
36 1
                Jam::association('hasmany', $this->_terms_items_association_options)
37
            );
38 1
    }
39
40 1
    public function builder_call_with_terms(Database_Query $builder, Jam_Event_Data $data, $term_slugs, $operator = 'IN', $nesting_level = 1)
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
41
    {
42 1
        if ($term_slugs)
43
        {
44 1
            if ( ! ($term_slugs instanceof Jam_Query_Builder_Collection)
45 1
             AND ! ($term_slugs instanceof Jam_Array_Association))
46
            {
47 1
                $term_slugs = Jam::all('term')
48 1
                    ->slugs_children($term_slugs)
49 1
                    ->order_by('id');
50
            }
51
52 1
            $terms = $term_slugs->as_array('id', 'slug');
53 1
            $terms_ids = array_keys($terms);
54
55 1
            if ($nesting_level > 1)
56
            {
57
                $terms = Jam::all('term')
58
                    ->slugs_children(array_values($terms))
59
                    ->order_by('id');
60
61
                $terms_ids = Arr::merge($terms_ids, $terms->ids());
62
            }
63
64 1
            if ($terms_ids)
0 ignored issues
show
Bug Best Practice introduced by
The expression $terms_ids of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
65
            {
66 1
                $unique_alias = 'terms-'.join('-', $terms_ids);
67
68
                $builder
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Database_Query as the method join() does only exist in the following sub-classes of Database_Query: Database_Query_Builder_Select, Jam_Query_Builder_Collection, Jam_Query_Builder_Join, Jam_Query_Builder_Select, Kohana_Database_Query_Builder_Select, Kohana_Jam_Query_Builder_Collection, Kohana_Jam_Query_Builder_Join, Kohana_Jam_Query_Builder_Select, Model_Collection_Test_Author. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
69 1
                    ->join(array('terms_items', $unique_alias))
70 1
                    ->on(
71 1
                        $unique_alias.'.term_id',
72
                        $operator,
73 1
                        DB::expr("(".join(',', $terms_ids).')')
74
                    );
75
            }
76
        }
77
78 1
        return $builder;
79
    }
80
}
81