Completed
Push — master ( af185c...141147 )
by Hannes
01:45
created

AccountFactory::createAccount()   C

Complexity

Conditions 9
Paths 8

Size

Total Lines 66
Code Lines 38

Duplication

Lines 34
Ratio 51.52 %

Code Coverage

Tests 29
CRAP Score 9

Importance

Changes 0
Metric Value
dl 34
loc 66
ccs 29
cts 29
cp 1
rs 6.4099
c 0
b 0
f 0
cc 9
eloc 38
nc 8
nop 1
crap 9

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
namespace byrokrat\banking;
4
5
use byrokrat\banking\Rewriter\RewriterStrategy;
6
use byrokrat\banking\Rewriter\ClearingSeparatorRewriter;
7
use byrokrat\banking\Rewriter\SwedbankCheckDigitRewriter;
8
use byrokrat\banking\Exception\UnableToCreateAccountException;
9
use byrokrat\banking\Exception\InvalidAccountNumberException;
10
use byrokrat\banking\Exception\InvalidCheckDigitException;
11
12
/**
13
 * Account number factory
14
 */
15
class AccountFactory
16
{
17
    /**
18
     * @var Format[] Loaded formats
19
     */
20
    private $formats;
21
22
    /**
23
     * @var RewriterStrategy[] Strategies for rewriting failing account numbers
24
     */
25
    private $rewriteStrategies;
26
27
    /**
28
     * @var bool Flag if rewrites should be allowed when creating account objects
29
     */
30
    private $allowRewrites;
31
32
    /**
33
     * @var UnknownFormat The unknown format or null if unknown is disallowed
34
     */
35
    private $unknownFormat;
36
37
    /**
38
     * @param Format[]           $formats
39
     * @param RewriterStrategy[] $rewrites
40
     * @param boolean            $allowRewrites Flag if rewrites should be allowed when creating account objects
41
     * @param boolean            $allowUnknown  Flag if the unknown account format should be used
42
     */
43 10
    public function __construct(array $formats = [], array $rewrites = [], $allowRewrites = true, $allowUnknown = true)
44
    {
45 10
        $this->formats = $formats ?: (new Formats)->createFormats();
0 ignored issues
show
Documentation Bug introduced by
It seems like $formats ?: (new \byrokr...ats())->createFormats() of type array<integer|string,obj...rokrat\banking\Format>> is incompatible with the declared type array<integer,object<byrokrat\banking\Format>> of property $formats.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
46 10
        $this->rewriteStrategies = $rewrites ?: [new ClearingSeparatorRewriter, new SwedbankCheckDigitRewriter];
47 10
        $this->allowRewrites = $allowRewrites;
48 10
        $this->unknownFormat = $allowUnknown ? new UnknownFormat : null;
49 10
    }
50
51
    /**
52
     * Enable formats
53
     *
54
     * Please note that formats not listed will be dropped and
55
     * can not be recovered.
56
     *
57
     * @param  string[] $formats List of formats to whitelist
58
     * @return null
59
     */
60 1
    public function whitelistFormats(array $formats)
61
    {
62 1
        foreach ($this->formats as $formatId => $format) {
63 1
            if (!in_array($formatId, $formats)) {
64 1
                unset($this->formats[$formatId]);
65
            }
66
        }
67 1
    }
68
69
    /**
70
     * Disable formats
71
     *
72
     * Please note that listed formats will be dropped and
73
     * can not be recovered.
74
     *
75
     * @param  string[] $formats List of formats to blacklist
76
     * @return null
77
     */
78 1
    public function blacklistFormats(array $formats)
79
    {
80 1
        foreach ($formats as $format) {
81 1
            unset($this->formats[$format]);
82
        }
83 1
    }
84
85
    /**
86
     * Create bank account object using number
87
     *
88
     * @param  string $number
89
     * @return AccountNumber
90
     * @throws UnableToCreateAccountException If unable to create
91
     */
92 10
    public function createAccount($number)
93
    {
94 10
        $parseMap = $this->createParseMap($number);
95
96 10
        if (count($parseMap['success']) == 1) {
97 3
            return $parseMap['success'][0];
98
        }
99
100 7 View Code Duplication
        if (count($parseMap['success']) > 1) {
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...
101 1
            throw new UnableToCreateAccountException(
102
                sprintf(
103 1
                    'Unable to parse account %s, multiple matching formats: %s.',
104
                    $number,
105
                    implode(
106 1
                        ' and ',
107
                        array_map(
108
                            function (AccountNumber $account) {
109 1
                                return $account->getBankName();
110 1
                            },
111 1
                            $parseMap['success']
112
                        )
113
                    )
114
                )
115
            );
116
        }
117
118 6
        if (count($parseMap['rewrite']) == 1 && $this->allowRewrites) {
119 1
            return $parseMap['rewrite'][0];
120
        }
121
122 5 View Code Duplication
        if ($parseMap['rewrite']) {
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...
123 2
            throw new UnableToCreateAccountException(
124
                sprintf(
125 2
                    'Unable to parse account %s. You may rewrite number as: %s.',
126
                    $number,
127
                    implode(
128 2
                        ' or ',
129
                        array_map(
130
                            function (AccountNumber $account) {
131 2
                                return $account->getNumber();
132 2
                            },
133 2
                            $parseMap['rewrite']
134
                        )
135
                    )
136
                )
137
            );
138
        }
139
140 3
        if ($parseMap['exception']) {
141 1
            throw new UnableToCreateAccountException(
142 1
                "Unable to parse account $number: {$parseMap['exception']->getMessage()}",
143 1
                0,
144 1
                $parseMap['exception']
145
            );
146
        }
147
148 2
        if ($this->unknownFormat) {
149
            try {
150 2
                return $this->unknownFormat->parse($number);
151 1
            } catch (InvalidAccountNumberException $exception) {
152
                // ignore failure to parse unknown account
153
            }
154
        }
155
156 1
        throw new UnableToCreateAccountException("Unable to parse account $number.");
157
    }
158
159
    /**
160
     * @param  string $number
161
     * @return array
162
     */
163 10
    private function createParseMap($number)
164
    {
165
        $parseMap = [
166 10
            'success' => [],
167
            'rewrite' => [],
168
            'exception' => []
169
        ];
170
171 10
        $rewrites = array_map(
172 10
            function (RewriterStrategy $strategy) use ($number) {
173 10
                return $strategy->rewrite($number);
174 10
            },
175 10
            $this->rewriteStrategies
176
        );
177
178 10
        foreach ($this->formats as $format) {
179
            try {
180 10
                $parseMap['success'][] = $format->parse($number);
181 4
                continue;
182 7
            } catch (InvalidAccountNumberException $exception) {
183 7
                if ($exception instanceof InvalidCheckDigitException) {
184 1
                    $parseMap['exception'] = $exception;
185
                }
186
187 7
                foreach ($rewrites as $rewrite) {
188
                    try {
189 7
                        $parseMap['rewrite'][] = $format->parse($rewrite);
190 4
                    } catch (InvalidAccountNumberException $e) {
191 7
                        continue;
192
                    }
193
                }
194
            }
195
        }
196
197 10
        return $parseMap;
198
    }
199
}
200