Completed
Pull Request — master (#75)
by Tim
03:14
created

AbstractSelectCallback::handle()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 54
Code Lines 25

Duplication

Lines 29
Ratio 53.7 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 29
loc 54
ccs 0
cts 40
cp 0
rs 9.6716
cc 3
eloc 25
nc 3
nop 2
crap 12

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * TechDivision\Import\Callbacks\SelectCallback
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2016 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Callbacks;
22
23
use TechDivision\Import\Utils\MemberNames;
24
use TechDivision\Import\Utils\RegistryKeys;
25
use TechDivision\Import\Utils\StoreViewCodes;
26
27
/**
28
 * A callback implementation that converts the passed select value.
29
 *
30
 * @author    Tim Wagner <[email protected]>
31
 * @copyright 2016 TechDivision GmbH <[email protected]>
32
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
33
 * @link      https://github.com/techdivision/import
34
 * @link      http://www.techdivision.com
35
 */
36
abstract class AbstractSelectCallback extends AbstractCallback
37
{
38
39
    /**
40
     * Will be invoked by a observer it has been registered for.
41
     *
42
     * @param string $attributeCode  The code of the attribute the passed value is for
43
     * @param mixed  $attributeValue The value to handle
44
     *
45
     * @return mixed|null The modified value
46
     * @see \TechDivision\Import\Callbacks\CallbackInterface::handle()
47
     */
48
    public function handle($attributeCode, $attributeValue)
49
    {
50
51
        // load the store ID
52
        $storeId = $this->getStoreId(StoreViewCodes::ADMIN);
53
54
        // try to load the attribute option value and return the option ID
55
        if ($eavAttributeOptionValue = $this->loadEavAttributeOptionValueByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $attributeValue)) {
56
            return $eavAttributeOptionValue[MemberNames::OPTION_ID];
57
        }
58
59
        // query whether or not we're in debug mode
60 View Code Duplication
        if ($this->isDebugMode()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
61
            // log a warning and return immediately
62
            $this->getSystemLogger()->warning(
63
                $this->appendExceptionSuffix(
64
                    sprintf(
65
                        'Can\'t find select option value "%s" for attribute %s',
66
                        $attributeValue,
67
                        $attributeCode
68
                    )
69
                )
70
            );
71
72
            // add the missing option value to the registry
73
            $this->mergeAttributesRecursive(
74
                array(
75
                    RegistryKeys::MISSING_OPTION_VALUES => array(
76
                        $attributeCode => array(
77
                            $attributeValue => array(
78
                                $this->raiseCounter($attributeValue),
79
                                array($this->getUniqueIdentifier() => true)
80
                            )
81
                        )
82
                    )
83
                )
84
            );
85
86
            // return NULL, if the value can't be mapped to an option
87
            return;
88
        }
89
90
        // throw an exception if the attribute is NOT
91
        // available and we're not in debug mode
92
        throw new \Exception(
93
            $this->appendExceptionSuffix(
94
                sprintf(
95
                    'Can\'t find select option value "%s" for attribute %s',
96
                    $attributeValue,
97
                    $attributeCode
98
                )
99
            )
100
        );
101
    }
102
103
    /**
104
     * Load's and return's the EAV attribute option value with the passed code, store ID and value.
105
     *
106
     * @param string  $attributeCode The code of the EAV attribute option to load
107
     * @param integer $storeId       The store ID of the attribute option to load
108
     * @param string  $value         The value of the attribute option to load
109
     *
110
     * @return array The EAV attribute option value
111
     */
112
    protected function loadEavAttributeOptionValueByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value)
113
    {
114
        return $this->getSubject()->loadEavAttributeOptionValueByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method loadEavAttributeOptionVa...odeAndStoreIdAndValue() does only exist in the following implementations of said interface: TechDivision\Import\Subjects\AbstractEavSubject.

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...
115
    }
116
}
117