Completed
Push — master ( 110a17...f6145a )
by Christophe
03:27
created

Chart::startDraw()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
cc 3
eloc 15
nc 3
nop 0
1
<?php
2
3
namespace CMEN\GoogleChartsBundle\GoogleCharts;
4
5
use CMEN\GoogleChartsBundle\Exception\GoogleChartsException;
6
use CMEN\GoogleChartsBundle\GoogleCharts\Charts\Diff\DiffChart;
7
use CMEN\GoogleChartsBundle\GoogleCharts\Options\ChartOptionsDraw;
8
9
/**
10
 * @author Christophe Meneses
11
 */
12
abstract class Chart
13
{
14
    /**
15
     * @var string
16
     */
17
    protected $elementID;
18
19
    /**
20
     * @var Data
21
     */
22
    protected $data;
23
24
    /**
25
     * @var ChartOptionsDraw
26
     */
27
    protected $options;
28
29
    /**
30
     * @var Events
31
     */
32
    protected $events;
33
34
35
    public function __construct()
36
    {
37
        $this->data = new Data();
38
        $this->events = new Events($this);
39
    }
40
41
42
    /**
43
     * Returns chart's name.
44
     *
45
     * @return string
46
     */
47
    public function getName()
48
    {
49
        return 'chart' . ucfirst($this->elementID);
50
    }
51
52
    /**
53
     * Returns data chart's name
54
     *
55
     * @return string
56
     */
57
    public function getDataName()
58
    {
59
        return 'data'. ucfirst($this->getName());
60
    }
61
62
    /**
63
     * Returns options chart's name
64
     *
65
     * @return string
66
     */
67
    public function getOptionsName()
68
    {
69
        return 'options'. ucfirst($this->getName());
70
    }
71
72
    /**
73
     * Returns the chart type.
74
     *
75
     * @return string
76
     */
77
    abstract protected function getType();
78
79
    /**
80
     * Returns the chart package.
81
     *
82
     * @return string
83
     */
84
    abstract public function getPackage();
85
86
    /**
87
     * Returns the instance options.
88
     */
89
    abstract public function getOptions();
90
91
    /**
92
     * Sets the instance Options
93
     *
94
     * @param ChartOptionsDraw $options
95
     *
96
     * @return ChartOptionsDraw
97
     */
98
    abstract public function setOptions($options);
99
100
    /**
101
     * Returns the Javascript of the beginning of the chart (Declaration, data and options).
102
     *
103
     * @return string Javascript
104
     *
105
     * @throws GoogleChartsException
106
     */
107
    public function startDraw()
108
    {
109
        if ($this->elementID === null) {
110
            throw new GoogleChartsException('Container is not defined.');
111
        }
112
113
        $js = 'var ' . $this->getName() . ' = new google.visualization.' . $this->getType() .
114
            '(document.getElementById("' . $this->elementID . '"));';
115
116
        if (!$this instanceof DiffChart) {
117
            $js .= $this->data->draw($this->getDataName());
118
        } else {
119
            $js .= $this->getOldChart()->getData()->draw('old_' . $this->getDataName()) .
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class CMEN\GoogleChartsBundle\GoogleCharts\Chart as the method getOldChart() does only exist in the following sub-classes of CMEN\GoogleChartsBundle\GoogleCharts\Chart: CMEN\GoogleChartsBundle\...harts\Diff\DiffBarChart, CMEN\GoogleChartsBundle\...ts\Diff\DiffColumnChart, CMEN\GoogleChartsBundle\...harts\Diff\DiffPieChart, CMEN\GoogleChartsBundle\...s\Diff\DiffScatterChart. 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...
120
                $this->getNewChart()->getData()->draw('new_' . $this->getDataName()) .
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class CMEN\GoogleChartsBundle\GoogleCharts\Chart as the method getNewChart() does only exist in the following sub-classes of CMEN\GoogleChartsBundle\GoogleCharts\Chart: CMEN\GoogleChartsBundle\...harts\Diff\DiffBarChart, CMEN\GoogleChartsBundle\...ts\Diff\DiffColumnChart, CMEN\GoogleChartsBundle\...harts\Diff\DiffPieChart, CMEN\GoogleChartsBundle\...s\Diff\DiffScatterChart. 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...
121
                'var ' . $this->getDataName() . ' = ' . $this->getName() .
122
                    '.computeDiff(old_' . $this->getDataName() . ',
123
                 new_' . $this->getDataName() . ');';
124
        }
125
126
        $js .= $this->options->draw($this->getOptionsName());
127
128
        return $js;
129
    }
130
131
    /**
132
     * Returns the Javascript of the end of the chart (Events and drawing).
133
     *
134
     * @return string
135
     */
136
    public function endDraw()
137
    {
138
        return $this->events->draw(). $this->getName() .
139
            '.draw('. $this->getDataName() . ', '. $this->getOptionsName() .');';
140
    }
141
142
    /**
143
     * @param string $elementID
144
     *
145
     * @return $this
146
     */
147
    public function setElementID($elementID)
148
    {
149
        $this->elementID = $elementID;
150
151
        return $this;
152
    }
153
154
    /**
155
     * @return string
156
     */
157
    public function getElementID()
158
    {
159
        return $this->elementID;
160
    }
161
162
    /**
163
     * @return Data
164
     */
165
    public function getData()
166
    {
167
        return $this->data;
168
    }
169
170
    /**
171
     * @return Events
172
     */
173
    public function getEvents()
174
    {
175
        return $this->events;
176
    }
177
}
178