Completed
Push — master ( 8c66d9...9360f0 )
by Hannes
03:21 queued 02:00
created

AccountFactory::createAccount()   D

Complexity

Conditions 15
Paths 128

Size

Total Lines 100
Code Lines 59

Duplication

Lines 34
Ratio 34 %

Code Coverage

Tests 46
CRAP Score 15

Importance

Changes 0
Metric Value
dl 34
loc 100
ccs 46
cts 46
cp 1
rs 4.597
c 0
b 0
f 0
cc 15
eloc 59
nc 128
nop 1
crap 15

How to fix   Long Method    Complexity   

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\RewriterFactory;
7
use byrokrat\banking\Exception\UnableToCreateAccountException;
8
use byrokrat\banking\Exception\InvalidAccountNumberException;
9
use byrokrat\banking\Exception\InvalidCheckDigitException;
10
11
/**
12
 * Account number factory
13
 */
14
class AccountFactory
15
{
16
    /**
17
     * @var Format[] Possible formats used when parsing account
18
     */
19
    private $formats;
20
21
    /**
22
     * @var RewriterStrategy[] Strategies for rewriting failing account numbers
23
     */
24
    private $rewrites;
25
26
    /**
27
     * @var bool Flag if rewrites should be allowed when creating account objects
28
     */
29
    private $allowRewrites;
30
31
    /**
32
     * @var UnknownFormat The unknown format or null if unknown is disallowed
33
     */
34
    private $unknownFormat;
35
36
    /**
37
     * @var RewriterStrategy[] Preprocessors used to alter account before parsing starts
38
     */
39
    private $preprocessors;
40
41
    /**
42
     * @param Format[]           $formats       Possible formats used when parsing account
43
     * @param RewriterStrategy[] $rewrites      Rewrites used if parsed the raw account number fails
44
     * @param boolean            $allowRewrites Flag if rewrites should be allowed when creating account objects
45
     * @param boolean            $allowUnknown  Flag if the unknown account format should be used
46
     * @param RewriterStrategy[] $preprocessors Preprocessors used to alter account before parsing starts
47
     */
48 110
    public function __construct(
49
        array $formats = [],
50
        array $rewrites = null,
51
        $allowRewrites = true,
52
        $allowUnknown = true,
53
        $preprocessors = null
54
    ) {
55 110
        $this->formats = $formats ?: (new FormatFactory)->createFormats();
0 ignored issues
show
Documentation Bug introduced by
It seems like $formats ?: (new \byrokr...ory())->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...
56 110
        $this->rewrites = is_array($rewrites) ? $rewrites : (new RewriterFactory)->createRewrites();
57 110
        $this->allowRewrites = $allowRewrites;
58 110
        $this->unknownFormat = $allowUnknown ? new UnknownFormat : null;
59 110
        $this->preprocessors = is_array($preprocessors) ? $preprocessors : (new RewriterFactory)->createPreprocessors();
60 110
    }
61
62
    /**
63
     * Enable formats
64
     *
65
     * Please note that formats not listed will be dropped and
66
     * can not be recovered.
67
     *
68
     * @param  string[] $formats List of formats to whitelist
69
     * @return null
70
     */
71 1
    public function whitelistFormats(array $formats)
72
    {
73 1
        foreach ($this->formats as $formatId => $format) {
74 1
            if (!in_array($formatId, $formats)) {
75 1
                unset($this->formats[$formatId]);
76
            }
77
        }
78 1
    }
79
80
    /**
81
     * Disable formats
82
     *
83
     * Please note that listed formats will be dropped and
84
     * can not be recovered.
85
     *
86
     * @param  string[] $formats List of formats to blacklist
87
     * @return null
88
     */
89 100
    public function blacklistFormats(array $formats)
90
    {
91 100
        foreach ($formats as $format) {
92 18
            unset($this->formats[$format]);
93
        }
94 100
    }
95
96
    /**
97
     * Create bank account object using number
98
     *
99
     * @param  string $number
100
     * @return AccountNumber
101
     * @throws UnableToCreateAccountException If unable to create
102
     */
103 110
    public function createAccount($number)
104
    {
105 110
        foreach ($this->preprocessors as $preprocessor) {
106 105
            $number = $preprocessor->rewrite($number);
107
        }
108
109
        $parseMap = [
110 110
            'success' => [],
111
            'rewrite' => [],
112
            'exception' => []
113
        ];
114
115 110
        $rewrites = array_map(
116
            function (RewriterStrategy $strategy) use ($number) {
117 104
                return $strategy->rewrite($number);
118 110
            },
119 110
            $this->rewrites
120
        );
121
122 110
        foreach ($this->formats as $format) {
123
            try {
124 110
                $parseMap['success'][] = $format->parse($number);
125 101
                continue;
126 106
            } catch (InvalidAccountNumberException $exception) {
127 106
                if ($exception instanceof InvalidCheckDigitException) {
128 2
                    $parseMap['exception'] = $exception;
129
                }
130
131 106
                foreach ($rewrites as $rewrite) {
132
                    try {
133 103
                        $parseMap['rewrite'][] = $format->parse($rewrite);
134 100
                    } catch (InvalidAccountNumberException $e) {
135 106
                        continue;
136
                    }
137
                }
138
            }
139
        }
140
141 110
        if (count($parseMap['success']) == 1) {
142 100
            return $parseMap['success'][0];
143
        }
144
145 11 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...
146 1
            throw new UnableToCreateAccountException(
147
                sprintf(
148 1
                    'Unable to parse account %s, multiple matching formats: %s.',
149
                    $number,
150
                    implode(
151 1
                        ' and ',
152
                        array_map(
153
                            function (AccountNumber $account) {
154 1
                                return $account->getBankName();
155 1
                            },
156 1
                            $parseMap['success']
157
                        )
158
                    )
159
                )
160
            );
161
        }
162
163 10
        if (count($parseMap['rewrite']) == 1 && $this->allowRewrites) {
164 2
            return $parseMap['rewrite'][0];
165
        }
166
167 8 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...
168 2
            throw new UnableToCreateAccountException(
169
                sprintf(
170 2
                    'Unable to parse account %s. You may rewrite number as: %s.',
171
                    $number,
172
                    implode(
173 2
                        ' or ',
174
                        array_map(
175 2
                            function (AccountNumber $account) {
176 2
                                return $account->getNumber();
177 2
                            },
178 2
                            $parseMap['rewrite']
179
                        )
180
                    )
181
                )
182
            );
183
        }
184
185 6
        if ($parseMap['exception']) {
186 1
            throw new UnableToCreateAccountException(
187 1
                "Unable to parse account $number: {$parseMap['exception']->getMessage()}",
188 1
                0,
189 1
                $parseMap['exception']
190
            );
191
        }
192
193 5
        if ($this->unknownFormat) {
194
            try {
195 5
                return $this->unknownFormat->parse($number);
196 1
            } catch (InvalidAccountNumberException $exception) {
197
                // ignore failure to parse unknown account
198
            }
199
        }
200
201 1
        throw new UnableToCreateAccountException("Unable to parse account $number.");
202
    }
203
}
204