MsProjectMPX   F
last analyzed

Complexity

Total Complexity 72

Size/Duplication

Total Lines 332
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 72
lcom 1
cbo 3
dl 0
loc 332
ccs 165
cts 165
cp 1
rs 2.64
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A canRead() 0 18 6
D load() 0 62 22
A readRecord30() 0 39 3
B readRecord41() 0 20 6
A readRecord50() 0 8 2
C readRecord61() 0 44 16
B readRecord70() 0 30 10
B readRecord75() 0 28 6

How to fix   Complexity   

Complex Class

Complex classes like MsProjectMPX often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MsProjectMPX, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * This file is part of PHPProject - A pure PHP library for reading and writing
4
 * presentations documents.
5
 *
6
 * PHPProject is free software distributed under the terms of the GNU Lesser
7
 * General Public License version 3 as published by the Free Software Foundation.
8
 *
9
 * For the full copyright and license information, please read the LICENSE
10
 * file that was distributed with this source code. For the full list of
11
 * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
12
 *
13
 * @link        https://github.com/PHPOffice/PHPProject
14
 * @copyright   2009-2014 PHPProject contributors
15
 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16
 */
17
18
namespace PhpOffice\PhpProject\Reader;
19
20
use PhpOffice\PhpProject\PhpProject;
21
use PhpOffice\PhpProject\Task;
22
23
/**
24
 * MPX File Format
25
 * @link http://support.microsoft.com/kb/270139/en-us
26
 * @category    PHPProject
27
 * @package        PHPProject
28
 * @copyright    Copyright (c) 2012 - 2012 PHPProject (https://github.com/PHPOffice/PHPProject)
29
 */
30
class MsProjectMPX
31
{
32
    /**
33
     * PHPProject object
34
     *
35
     * @var \PhpOffice\PhpProject\PhpProject
36
     */
37
    private $phpProject;
38
    
39
    /**
40
     * Numeric Resource Table
41
     * @var string[]
42
     */
43
    private $defResource = array();
44
    
45
    /**
46
     * Numeric Table Table
47
     * @var string[]
48
     */
49
    private $defTask = array();
50
    
51
    /**
52
     * Index in $defTask for the precedessor
53
     * @var integer
54
     */
55
    private $iParentTaskIdx;
56
    
57
    /**
58
     * last Task created
59
     * @var Task
60
     */
61
    private $oPreviousTask;
62
    
63
    /**
64
     * Create a new GanttProject
65
     */
66 3
    public function __construct()
67
    {
68 3
        $this->phpProject = new PhpProject();
69 3
    }
70
    /**
71
     *
72
     * @param string $pFilename
73
     * @return PHPProject
74
     */
75 3
    public function canRead($pFilename)
76
    {
77 3
        if (!file_exists($pFilename) || !is_readable($pFilename)) {
78 2
            return false;
79
        }
80 2
        $sContent = file_get_contents($pFilename);
81 2
        $arrayLines = explode(PHP_EOL, $sContent);
82
        
83
        // The only required record is the File Creation record
84 2
        foreach ($arrayLines as $sLine) {
85 2
            $arrayRecord = explode(';', $sLine);
86 2
            if (!is_numeric($arrayRecord[0]) && $arrayRecord[0] == 'MPX') {
87 2
                return true;
88
            }
89 1
        }
90
        
91 1
        return false;
92
    }
93
    
94
    /**
95
     * 
96
     * @param string $pFilename
97
     * @throws \Exception
98
     * @return PHPProject|null
99
     */
100 2
    public function load($pFilename)
101
    {
102 2
        if (!$this->canRead($pFilename)) {
103 1
            throw new \Exception('The file is not accessible.');
104
        }
105 1
        $sContent = file_get_contents($pFilename);
106 1
        $arrayLines = explode(PHP_EOL, $sContent);
107
        
108 1
        foreach ($arrayLines as $sLine) {
109 1
            $arrayRecord = explode(';', $sLine);
110 1
            switch ($arrayRecord[0]) {
111 1
                case 'MPX': // File Creation
112 1
                case '10': // Currency Settings
113 1
                case '11': // Default Settings
114 1
                case '12': // Date and Time Settings
115 1
                case '20': // Base Calendar Definition
116 1
                case '25': // Base Calendar Hours
117 1
                case '26': // Base Calendar Exception
118 1
                    break;
119 1
                case '30': // Project Header
120 1
                    $this->readRecord30($arrayRecord);
121 1
                    break;
122 1
                case '40': // Text Resource Table Definition
123
                    // Label for 41
124 1
                    break;
125 1
                case '41': // Numeric Resource Table Definition
126 1
                    $this->readRecord41($arrayRecord);
127 1
                    break;
128 1
                case '50': // Resource
129 1
                    $this->readRecord50($arrayRecord);
130 1
                    break;
131
                //case '51': // Resource Notes
132
                //case '55': // Resource Calendar Definition
133
                //case '56': // Resource Calendar Hours
134
                //case '57': // Resource Calendar Exception
135
                //    break;
136 1
                case '60': // Text Task Table Definition
137
                    // Label for 61
138 1
                    break;
139 1
                case '61': // Numeric Task Table Definition
140 1
                    $this->readRecord61($arrayRecord);
141 1
                    break;
142 1
                case '70': // Task
143 1
                    $this->readRecord70($arrayRecord);
144 1
                    break;
145
                //case '71': // Task Notes
146
                //case '72': // Recurring Task
147
                //    break;
148 1
                case '75': // Resource Assignment
149 1
                    $this->readRecord75($arrayRecord);
150 1
                    break;
151 1
                case '76': // Resource Assignment Workgroup Fields
152 1
                case '80': // Project Names
153 1
                case '81': // DDE and OLE Client Links
154 1
                case '0': // Comments
155 1
                default:
156
                    // throw new \Exception('load : Not implemented ('.$arrayRecord[0].')');
157 1
            }
158 1
        }
159
        
160 1
        return $this->phpProject;
161
    }
162
    
163
    /**
164
     * Project Header
165
     * @param array $record
166
     */
167 1
    private function readRecord30(array $record)
168
    {
169
        // 0 : Record
170
        // 1 : Project tab
171
        // 2 : Company
172
        // 3 : Manager
173
        // 4 : Calendar (Standard used if no entry)
174
        // 5 : Start Date (either this field or the next field is calculated for an imported file, depending on the Schedule From setting)
175 1
        if (isset($record[5]) && !empty($record[5])) {
176 1
            $this->phpProject->getInformations()->setStartDate($record[5]);
177 1
        }
178
        // 6 : Finish Date
179
        //if (isset($record[6]) && !empty($record[6])) {
180
        //    $this->phpProject->getInformations()->setEndDate($record[6]);
181
        //}
182
        // 7 : Schedule From (0 = start, 1 = finish)
183
        // 8 : Current Date*
184
        // 9 : Comments
185
        // 10 : Cost*
186
        // 11 : Baseline Cost
187
        // 12 : Actual Cost
188
        // 13 : Work
189
        // 14 : Baseline Work
190
        // 15 : Actual Work
191
        // 16 : Work
192
        // 17 : Duration
193
        // 18 : Baseline Duration
194
        // 19 : Actual Duration
195
        // 20 : Percent Complete
196
        // 21 : Baseline Start
197
        // 22 : Baseline Finish
198
        // 23 : Actual Start
199
        // 24 : Actual Finish
200
        // 25 : Start Variance
201
        // 26 : Finish Variance
202
        // 27 : Subject
203
        // 28 : Author
204
        // 29 : Keywords
205 1
    }
206
    
207
    /**
208
     * Numeric Resource Table Definition
209
     * @param array $record
210
     */
211 1
    private function readRecord41(array $record)
212
    {
213 1
        array_shift($record);
214 1
        foreach ($record as $key => $item) {
215
            switch ($item) {
216 1
                case 1: // Name
217 1
                    $this->defResource[$key+1] = 'setTitle';
218 1
                    break;
219 1
                case 40: // ID
220 1
                    $this->defResource[$key+1] = 'setIndex';
221 1
                    break;
222 1
                case 41: // Max Units
223 1
                    break;
224 1
                case 49: // Unique ID
225 1
                    break;
226
                //default:
227
                    //throw new \Exception('readRecord41 : Not implemented ('.$item.')');
228
            }
229 1
        }
230 1
    }
231
    
232
    /**
233
     * Resource
234
     * @param array $record
235
     */
236 1
    private function readRecord50(array $record)
237
    {
238 1
        $oResource = $this->phpProject->createResource();
239
        
240 1
        foreach ($this->defResource as $key => $method) {
241 1
            $oResource->{$method}($record[$key]);
242 1
        }
243 1
    }
244
    
245
    /**
246
     * Numeric Task Table Definition
247
     * @param array $record
248
     */
249 1
    private function readRecord61(array $record)
250
    {
251 1
        array_shift($record);
252 1
        foreach ($record as $key => $item) {
253
            switch ($item) {
254 1
                case 1: // Name
255 1
                    $this->defTask[$key + 1] = 'setName';
256 1
                    break;
257 1
                case 2: // WBS
258 1
                    break;
259 1
                case 3: // Outline Level
260 1
                    break;
261 1
                case 40: // Duration
262 1
                    $this->defTask[$key + 1] = 'setDuration';
263 1
                    break;
264 1
                case 44: // % Complete
265 1
                    $this->defTask[$key + 1] = 'setProgress';
266 1
                    break;
267 1
                case 50: // Start
268 1
                    $this->defTask[$key + 1] = 'setStartDate';
269 1
                    break;
270 1
                case 58: // Actual Start
271 1
                    break;
272 1
                case 70: // Predecessors
273 1
                    $this->iParentTaskIdx = $key + 1;
0 ignored issues
show
Documentation Bug introduced by
It seems like $key + 1 can also be of type double. However, the property $iParentTaskIdx is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
274 1
                    break;
275 1
                case 80: // Fixed
276 1
                    break;
277 1
                case 90: // ID
278 1
                    $this->defTask[$key + 1] = 'setIndex';
279 1
                    break;
280 1
                case 91: // Constraint Type
281 1
                    break;
282 1
                case 98: // Unique ID
283 1
                    break;
284 1
                case 99: // Outline Number
285 1
                    break;
286 1
                case 120: // Summary
287 1
                    break;
288
                //default:
289
                    //throw new \Exception('readRecord41 : Not implemented ('.$item.')');
290
            }
291 1
        }
292 1
    }
293
    
294
    /**
295
     * Task
296
     * @param array $record
297
     */
298 1
    private function readRecord70(array $record)
299
    {
300 1
        $oTask = null;
301 1
        if (!is_null($this->iParentTaskIdx) && !empty($record[$this->iParentTaskIdx])) {
302 1
            $oTaskParent = $this->phpProject->getTaskFromIndex($record[$this->iParentTaskIdx]);
303 1
            if (is_object($oTaskParent)) {
304 1
                $oTask = $oTaskParent->createTask();
305 1
            }
306 1
        }
307 1
        if (is_null($oTask)) {
308 1
            $oTask = $this->phpProject->createTask();
309 1
        }
310
        
311 1
        foreach ($this->defTask as $key => $method) {
312 1
            if ($method == 'setDuration') {
313 1
                if (substr($record[$key], -1) == 'd') {
314 1
                    $record[$key] = intval(substr($record[$key], 0, -1));
315 1
                }
316 1
            }
317 1
            if ($method == 'setProgress') {
318 1
                if (substr($record[$key], -1) == '%') {
319 1
                    $record[$key] = substr($record[$key], 0, -1);
320 1
                    $record[$key] = str_replace(',', '.', $record[$key]);
321 1
                    $record[$key] = floatval($record[$key]) / 100;
322 1
                }
323 1
            }
324 1
            $oTask->{$method}($record[$key]);
325 1
        }
326 1
        $this->oPreviousTask = $oTask;
327 1
    }
328
    
329
    /**
330
     * Resource Assignment
331
     * @param array $record
332
     */
333 1
    private function readRecord75(array $record)
334
    {
335
        // 0 : Record
336
        // 1 : ID
337 1
        $idResource = null;
338 1
        if (isset($record[1]) && !empty($record[1])) {
339 1
            $idResource = $record[1];
340 1
        }
341
        // 2 : Units
342
        // 3 : Work
343
        // 4 : Planned Work
344
        // 5 : Actual Work
345
        // 6 : Overtime Work
346
        // 7 : Cost
347
        // 8 : Planned Cost
348
        // 9 : Actual Cost
349
        // 10 : Start*
350
        // 11 : Finish*
351
        // 12 : Delay
352
        // 13 : Resource Unique ID
353
        
354 1
        if (!is_null($idResource) && $this->oPreviousTask instanceof Task) {
355 1
            $oResource = $this->phpProject->getResourceFromIndex($idResource);
356 1
            if (!is_null($oResource)) {
357 1
                $this->oPreviousTask->addResource($oResource);
0 ignored issues
show
Documentation introduced by
$oResource is of type resource, but the function expects a object<PhpOffice\PhpProject\Resource>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
358 1
            }
359 1
        }
360 1
    }
361
}
362