Potato::__set()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 1
Metric Value
c 1
b 1
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
3
namespace Ganga\Potato;
4
5
use ReflectionClass;
6
use PDO;
7
8
/**
9
 * Class that defines Potato ORM
10
 * Will be extended by Model Classes
11
 */
12
class Potato
13
{
14
    protected $tableName;
15
    protected static $id = null;
16
    protected static $table;
17
    protected $props = [];
18
19
    /**
20
     * Constructor
21
     * TableName is set when the class is extended
22
     */
23
    public function __construct()
24
    {
25
        /**
26
         * Tests if the Model has defined a table name
27
         * if not it assigns it to the name of the class
28
         */
29
        if (!$this->tableName) {
30
            $ref = new ReflectionClass($this);
31
            $tableName = strtolower($ref->getShortName()).'s';
32
        }
33
34
        // Test if table exists else throw an exception
35
        if (!self::tableExists($tableName)) {
36
            throw new PotatoException("Table $tableName does not exist.");
37
        } else {
38
            $this->tableName = $tableName;
0 ignored issues
show
Bug introduced by
The variable $tableName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
39
            self::$table = $tableName;
40
        }
41
    }
42
43
    public function __set($key, $value)
44
    {
45
        $this->props[$key] = $value;
46
    }
47
48
    /**
49
     * Checks if a given table exists in the database
50
     * @param  string $tableName name of the table to be checked
51
     * @return bool           true if table is found, false otherwise
52
     */
53
    public static function tableExists($tableName)
54
    {
55
        $query = "SELECT 1 FROM $tableName LIMIT 1";
56
57
        try {
58
            $result = Connection::db()->query($query);
59
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class Ganga\Potato\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
60
            return false;
61
        }
62
63
        return $result !== false;
64
    }
65
66
    /**
67
     * Get the name of the current table
68
     * @return String table name
69
     */
70
    public function getTableName()
71
    {
72
        return $this->tableName;
73
    }
74
75
    /**
76
     * Getting table names from a class
77
     * This is important when no instance of the class is defined
78
     * Like User::getAll()
79
     * @return string table name
80
     */
81
    public static function getTableNameFromClass()
82
    {
83
        $instance = new static();
84
        $ref = new ReflectionClass($instance);
85
        $name = $ref->getShortName();
86
        $table = strtolower($name) . 's';
87
88
        if (!self::tableExists($table)) {
89
            throw new PotatoException("Table $table does not exist.");
90
        }
91
92
        return $table;
93
    }
94
95
    /**
96
     * Get all records from the record table
97
     * @param array $fields array of fields to be retrieved
98
     * @return array associative array of the records received from the database
99
     */
100
    public static function getAll($fields = null)
101
    {
102
        $table = self::getTableNameFromClass();
103
104 View Code Duplication
        if ($fields != null && is_array($fields)) {
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...
105
            $query =  "SELECT ". implode(', ', $fields) ." FROM $table";
106
        } else {
107
            $query = "SELECT * FROM $table";
108
        }
109
110
        $res = [];
111
112
        $results = Connection::db()->query($query);
113
        $results->setFetchMode(PDO::FETCH_ASSOC);
114
115
        while ($row = $results->fetch()) {
116
            array_push($res, $row);
117
        }
118
119
        return $res;
120
    }
121
122
    /**
123
     * Get one record from the table based on the id provided
124
     * @param  integer $id id of the record to be retrieved
125
     * @param array $fields array of fields to be retrieved
126
     * @return array     associative array of the record retrieved
127
     */
128
    public static function getOne($id, $fields = null)
129
    {
130
        $table = self::getTableNameFromClass();
131
132 View Code Duplication
        if ($fields != null && is_array($fields)) {
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...
133
            $query = "SELECT ". implode(', ', $fields) ." FROM $table WHERE id = $id";
134
        } else {
135
            $query = "SELECT * FROM $table WHERE id = $id";
136
        }
137
138
        $res = [];
139
140
        $results = Connection::db()->query($query);
141
        $results->setFetchMode(PDO::FETCH_ASSOC);
142
143
        while ($row = $results->fetch()) {
144
            array_push($res, $row);
145
        }
146
147
        if (!empty($res)) {
148
            return $res;
149
        } else {
150
            throw new PotatoException('There is no user with id '. $id);
151
        }
152
    }
153
154
    /**
155
     * Save either a new or existing record
156
     * @return integer 1 is successful, 0 if false
157
     */
158
    public function save()
159
    {
160
        $result;
0 ignored issues
show
Bug introduced by
The variable $result seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
161
        if (! self::$id) {
162
            // new record so we insert
163
            $query = "INSERT INTO $this->tableName (".implode(", ", array_keys($this->props)).") VALUES(";
164
165
            for ($i = 0; $i < count($this->props); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
166
                $query .= "?";
167
168
                if ($i != count($this->props) - 1) {
169
                    $query .= ",";
170
                }
171
            }
172
173
            $query .= ");";
174
175
            $insert = Connection::db()->prepare($query);
176
            $result = $insert->execute(array_values($this->props));
177
        } else {
178
            // existing record, se we update
179
            $query = "UPDATE $this->tableName SET ";
180
181
            $count = 0;
182
            foreach ($this->props as $key => $value) {
183
                $query .= "$key = '$value'";
184
185
                if ($count != count($this->props) - 1) {
186
                    $query .= ",";
187
                }
188
                $count++;
189
            }
190
191
            $query .= " WHERE id = ".self::$id;
192
            $result = Connection::db()->query($query);
193
        }
194
195
        if (! $result) {
196
            return false;
197
        } else {
198
            return true;
199
        }
200
    }
201
202
    /**
203
     * Find a record by id and change static id param
204
     * @param  int $id id of the record to be found
205
     * @return ClassInstance     an instance of the TableClass so that properties can be assigned automatically
206
     */
207
    public static function find($id)
208
    {
209
        self::getOne($id);
210
        self::$id = $id;
211
        return new static();
212
    }
213
214
    /**
215
     * Delete a record by id from the table in context
216
     * @param  id $id the id of the record to be deleted
217
     * @return [type]     [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
218
     */
219
    public static function destroy($id)
220
    {
221
        $table = self::getTableNameFromClass();
222
223
        self::getOne($id);
224
225
        $query = "DELETE FROM $table WHERE id = ".$id;
226
227
        try {
228
            Connection::db()->query($query);
229
            return true;
230
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class Ganga\Potato\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
231
            return false;
232
        }
233
    }
234
}
235