Issues (1752)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Fwlib/Db/SyncDbSchema.php (7 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace Fwlib\Db;
3
4
use Fwlib\Bridge\Adodb;
5
use Fwlib\Util\UtilContainerAwareTrait;
6
7
/**
8
 * Db schema synchronize & update tools
9
 *
10
 * Define schema operate SQL in array, and track their execute status with a
11
 * log table.
12
 *
13
 * All SQL MUST execute by defined order, so when a SQL with maximum id is
14
 * done, all SQL before it was done.
15
 *
16
 * Execute of SQL will stop when got error, after SQL fixed, next execute will
17
 * automatic clear error SQL, or update it.
18
 *
19
 * SQL identify by id, so don't change them except you know what you are
20
 * doing. Id can start from 0 or 1, but can only assign by ascending order.
21
 *
22
 * If there are too many schema SQL, put altogether in one define file will
23
 * cost more memory and i/o. In this situation, you can split SQL define file
24
 * to small ones by step(eg: 100), then use getLastIdDone() to help check
25
 * which file to require.
26
 *
27
 * Other tools similar:
28
 * @link http://xml2ddl.berlios.de/
29
 *
30
 * @copyright   Copyright 2006-2015 Fwolf
31
 * @license     http://www.gnu.org/licenses/lgpl.html LGPL-3.0+
32
 */
33
class SyncDbSchema
34
{
35
    use AdodbAwareTrait {
36
        setDb as setDbInstance;
37
    }
38
    use UtilContainerAwareTrait;
39
40
41
    /**
42
     * Last schema SQL id
43
     *
44
     * @var int
45
     */
46
    public $lastId = -1;
47
48
    /**
49
     * Last schema SQL id which is executed
50
     *
51
     * @var int
52
     */
53
    public $lastIdDone = -1;
54
55
    /**
56
     * Table to track schema SQL execute status
57
     *
58
     * It should not include space in table name.
59
     *
60
     * In running product environment, if this table name changed, remember to
61
     * rename corresponding table in dbms.
62
     *
63
     * @var string
64
     */
65
    protected $logTable = 'log_sync_db_schema';
66
67
68
    /**
69
     * Check and create log table if not exists
70
     */
71
    public function checkLogTable()
72
    {
73
        $table = &$this->logTable;
74
        $db = $this->getDb();
75
76
        if (! $db->isTableExist($table)) {
77
            // SQL for Create table diffs by db type
78
            // @codeCoverageIgnoreStart
79
            if ($db->isDbSybase()) {
80
                $sql = "
81
CREATE TABLE $table (
82
    id      INT NOT NULL,
83
    done    INT DEFAULT 0,  /* 0:not do, -1:error, 1:done ok */
84
    sqltext TEXT,
85
    ts      TIMESTAMP NOT NULL,
86
    CONSTRAINT PK_$table PRIMARY KEY (id)
87
)
88
";
89
            } elseif ($db->isDbMysql()) {
90
                $sql = "
91
CREATE TABLE $table (
92
    id      INT(8) NOT NULL,
93
    done    TINYINT DEFAULT 0,   /* 0:not do, -1:error, 1:done ok */
94
    sqltext TEXT,
95
    ts      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
96
    PRIMARY KEY (id)
97
)
98
";
99
            } else {
100
                $sql = "
101
CREATE TABLE $table (
102
    id      INT NOT NULL,
103
    done    INT DEFAULT 0,   /* 0:not do, -1:error, 1:done ok */
104
    sqltext TEXT,
105
    PRIMARY KEY (id)
106
)
107
";
108
            }
109
            // @codeCoverageIgnoreEnd
110
111
            $db->execute($sql);
112
            if (0 < $db->getErrorCode()) {
113
                // @codeCoverageIgnoreStart
114
                $this->log(
115
                    $this->getDbError() . PHP_EOL .
116
                    "Log table $table does not exists and create fail."
117
                );
118
                exit;
119
                // @codeCoverageIgnoreEnd
120
            }
121
122
            $this->log("Log table $table does not exists, create it, done.");
123
124
        } else {
125
            $this->log("Log table $table already exists.");
126
127
            // Get last-done-id for later usage
128
            $this->getLastIdDone();
129
        }
130
    }
131
132
133
    /**
134
     * Delete SQL from the error one
135
     *
136
     * All SQL after the error one(will be un-executed) will be deleted,
137
     * ignore their execute status. This is good for debug, if you got an
138
     * error SQL, just fix it and call sync script again.
139
     */
140
    public function deleteErrorSql()
141
    {
142
        $db = $this->getDb();
143
144
        $sql = "SELECT id FROM $this->logTable WHERE done=-1 ORDER BY id ASC";
145
        $rs = $db->SelectLimit($sql, 1);
0 ignored issues
show
Documentation Bug introduced by
The method SelectLimit does not exist on object<Fwlib\Bridge\Adodb>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
146
147
        if (!$rs->EOF) {
148
            // Del SQL and SQL after it
149
            $id = $rs->fields['id'];
150
            $sql = "DELETE FROM {$this->logTable} WHERE id >= $id";
151
            $rs = $db->Execute($sql);
0 ignored issues
show
$rs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
152
153
            // Check result, should be greater than 0
154
            $i = $db->Affected_Rows();
0 ignored issues
show
Documentation Bug introduced by
The method Affected_Rows does not exist on object<Fwlib\Bridge\Adodb>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
155
            $this->log("Clear $i SQL start from failed SQL $id.");
156
        }
157
    }
158
159
160
    /**
161
     * Execute SQLs
162
     */
163
    public function execute()
164
    {
165
        // Clear previous failed SQL
166
        $this->deleteErrorSql();
167
168
        $db = $this->getDb();
169
170
        $sql = "SELECT id, sqltext FROM $this->logTable WHERE done<>1 ORDER BY id ASC";
171
        $rs = $db->Execute($sql);
172
173
        $cntDone = 0;
174
        while (!$rs->EOF) {
175
            $id = $rs->fields['id'];
176
            $sql = stripslashes($rs->fields['sqltext']);
177
178
            // Some DDL SQL can't use transaction, so do raw execute.
179
            $db->execute($sql);
180
181
            // Bad sybase support, select db will got error msg, eg:
182
            // Changed database context to 'db_name'
183
            // @codeCoverageIgnoreStart
184
            if ((0 == $db->getErrorCode()
185
                    && 0 == strlen($db->getErrorMessage()))
186
                || ('Changed database context t' ==
187
                    substr($db->getErrorMessage(), 0, 26))
188
            // @codeCoverageIgnoreEnd
189
            ) {
190
                $this->log("Execute SQL $id successful.");
191
                $this->setSqlStatus($id, 1);
192
                $this->lastIdDone = $id;
193
194
            } else {
195
                $this->log("Execute SQL $id failed.");
196
                $this->log($this->getDbError());
197
198
                $this->setSqlStatus($id, -1);
199
                $this->log("Execute abort.");
200
                return;
201
            }
202
203
            $cntDone ++;
204
            $rs->MoveNext();
205
        }
206
207
        if (0 == $cntDone) {
208
            $this->log('No un-done SQL to do.');
209
        } else {
210
            $this->log("Total $cntDone SQL executed successful.");
211
        }
212
    }
213
214
215
    /**
216
     * Get friendly db error msg
217
     *
218
     * @return  string
219
     */
220
    protected function getDbError()
221
    {
222
        $db = $this->getDb();
223
224
        return 'Error ' . $db->getErrorCode() .
225
            ': '  . $db->getErrorMessage();
226
    }
227
228
229
    /**
230
     * Get id of last SQL, ignore their execute status
231
     *
232
     * @return  int
233
     */
234 View Code Duplication
    public function getLastId()
0 ignored issues
show
This method seems to be duplicated in 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...
235
    {
236
        $db = $this->getDb();
237
238
        $sql = "SELECT id FROM $this->logTable ORDER BY id DESC";
239
        $rs = $db->SelectLimit($sql, 1);
0 ignored issues
show
Documentation Bug introduced by
The method SelectLimit does not exist on object<Fwlib\Bridge\Adodb>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
240
241
        if ($rs->EOF) {
242
            $id = -1;
243
        } else {
244
            $id = $rs->fields['id'];
245
        }
246
247
        $this->lastId = $id;
248
        return $id;
249
    }
250
251
252
    /**
253
     * Get id of last successful executed sql
254
     *
255
     * @return  int
256
     */
257 View Code Duplication
    public function getLastIdDone()
0 ignored issues
show
This method seems to be duplicated in 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...
258
    {
259
        $db = $this->getDb();
260
261
        $sql = "SELECT id FROM $this->logTable WHERE done=1 ORDER BY id DESC";
262
        $rs = $db->SelectLimit($sql, 1);
0 ignored issues
show
Documentation Bug introduced by
The method SelectLimit does not exist on object<Fwlib\Bridge\Adodb>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
263
264
        if ($rs->EOF) {
265
            $id = -1;
266
        } else {
267
            $id = $rs->fields['id'];
268
        }
269
270
        $this->lastIdDone = $id;
271
        return $id;
272
    }
273
274
275
    /**
276
     * Getter of $logTable
277
     *
278
     * @return  string
279
     */
280
    public function getLogTable()
281
    {
282
        return $this->logTable;
283
    }
284
285
286
    /**
287
     * Print log message
288
     *
289
     * @param   string  $msg
290
     * @param   boolean $newline
291
     */
292
    public function log($msg = '', $newline = true)
293
    {
294
        if ($newline) {
295
            $msg = $this->getUtilContainer()->getEnv()->ecl($msg, true);
296
        }
297
298
        echo $msg;
299
    }
300
301
302
    /**
303
     * @param   Adodb   $db
304
     * @return  static
305
     */
306
    public function setDb(Adodb $db)
307
    {
308
        $this->setDbInstance($db);
309
310
        $this->checkLogTable();
311
312
        return $this;
313
    }
314
315
316
    /**
317
     * @param   string  $logTable
318
     * @return  static
319
     */
320
    public function setLogTable($logTable)
321
    {
322
        $this->logTable = $logTable;
323
324
        return $this;
325
    }
326
327
328
    /**
329
     * Write sql to log table, without execute
330
     *
331
     * This method will call directly in schema SQL define file, one call for
332
     * one SQL, so it's hard to use db prepare for speed optimize, and it's a
333
     * little over design too.
334
     *
335
     * @param   int $id
336
     * @param   string  $sqltext
337
     */
338
    public function setSql($id, $sqltext)
339
    {
340
        $db = $this->getDb();
341
342
        if (-1 == $this->lastId) {
343
            $this->getLastId();
344
        }
345
346
        // Will not overwrite exists id.
347
        if ($id > $this->lastId) {
348
            $sqltext = addslashes($sqltext);
349
            $sqltext = $db->convertEncodingSql($sqltext);
350
351
            $sql = "INSERT INTO $this->logTable (id, sqltext)
352
VALUES ($id, '$sqltext')";
353
            $db->Execute($sql);
354
355
            if (0 != $db->getErrorCode()) {
356
                // Should not occur
357
                // @codeCoverageIgnoreStart
358
                $this->log($this->getDbError());
359
                $this->log('Store SQL failed.');
360
                exit;
361
                // @codeCoverageIgnoreEnd
362
363
            } else {
364
                $this->lastId = $id;
365
            }
366
        }
367
    }
368
369
370
    /**
371
     * Set status of a SQL stored in log table
372
     *
373
     * @param   int     $id
374
     * @param   int     $status {0: not_executed, 1: execute_ok, -1: execute_fail}
375
     */
376
    protected function setSqlStatus($id, $status)
377
    {
378
        $db = $this->getDb();
379
380
        $sql = "UPDATE $this->logTable SET done=$status WHERE id=$id";
381
        $db->Execute($sql);
382
    }
383
}
384