Completed
Push — release/1.0 ( 41b41a...7d7258 )
by Johnny
01:57
created

Database::write()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 39
Code Lines 22

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 39
rs 8.439
cc 6
eloc 22
nc 4
nop 1
1
<?php
2
require '../vendor/autoload.php';
3
4
use Redbox\Scan\Adapter;
5
use Redbox\Scan\Report\Report;
6
7
8
class Database extends \mysqli implements Adapter\AdapterInterface
9
{
10
    CONST SCAN_ID = 1;
11
12
    public function __construct($host, $user, $pass, $db)
13
    {
14
        parent::__construct($host, $user, $pass, $db);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class mysqli as the method __construct() does only exist in the following sub-classes of mysqli: Database. 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...
15
    }
16
17
    private function getScan()
18
    {
19
        $sql = sprintf("SELECT *, scandate as `date` FROM `scan` WHERE `id`='%s'", self::SCAN_ID);
20
        $result = $this->query($sql);
21
        if ($result) {
22
            return $result->fetch_assoc();
23
        }
24
        return false;
25
    }
26
27
    private function getReportItems()
28
    {
29
        $items = array();
30
        $sql = sprintf("SELECT * FROM `scanitems` WHERE `scanid`='%s'", self::SCAN_ID);
31
        $result = $this->query($sql);
32
        if ($result) {
33
            while ($item = $result->fetch_object()) {
34
                if (isset($items[$item->itemfolder]) == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
35
                    $items[$item->itemfolder] = array();
36
37
                $items[$item->itemfolder][$item->itemname] = $item->md5hash;
38
            }
39
        }
40
        return $items;
41
    }
42
43
    /**
44
     * Read the previous scan results from the file system.
45
     *
46
     * @return array
47
     */
48
    public function read() {
49
        $report = new Report($this->getScan());
50
        $report->setItems($this->getReportItems());
51
        return $report->toArray();
52
    }
53
54
    // TODO: This should be an universial exception
55
    /**
56
     * Write the report to the filesystem so we can reuse it
57
     * at a later stace when we invoke Redbox\Scan\ScanService's scan() method.
58
     *
59
     * @param Report|null $report
60
     * @return bool
61
     */
62
    public function write(Report $report = null) {
63
64
        if ($report) {
65
            $scandata = array(
66
                'name' => $report->getName(),
67
                'path' => $report->getPath(),
68
            );
69
70
            /* Step 1. Update the scan. */
71
            $sql = sprintf("UPDATE `scan` SET `name`='%s', `path`='%s', `scandate`=NOW()", $this->real_escape_string($scandata['name']), $this->real_escape_string($scandata['path']));
72
            $this->query($sql);
73
74
            if ($this->affected_rows > 0) {
75
                $items = $report->getItems();
76
                if (count($items) > 0) {
77
                    /* Step 2. Delete old items */
78
                    $sql = sprintf("DELETE FROM `scanitems` WHERE `scanid`='%s'", self::SCAN_ID);
79
                    $this->query($sql);
80
81
                    /* Step 3. Insert the new items */
82
                    foreach($items as $path => $item) {
83
                        foreach ($item as $filename => $md5hash) {
84
                            $sql = sprintf("INSERT INTO `scanitems` SET `scanid`='%s', `itemfolder`='%s', `itemname`='%s', `md5hash`='%s'",
85
                                self::SCAN_ID,
86
                                $this->real_escape_string($path),
87
                                $this->real_escape_string($filename),
88
                                $this->real_escape_string($md5hash)
89
90
                            );
91
                           $this->query($sql);
92
                        }
93
                    }
94
                }
95
96
            }
97
            return false;
98
        }
99
        return false;
100
    }
101
102
}
103
104
if (class_exists('mysqli')) {
105
106
107
    try {
108
109
        $path = dirname(__FILE__)."/assets";
110
        $tmpfile  = $path.'/new.tmp';
111
        $timefile = $path.'/time.txt';
112
113
        $databaseAdaptor = new Database(
114
            "localhost",
115
            "root",
116
            "root",
117
            "scan");
118
119
        $scan = new Redbox\Scan\ScanService($databaseAdaptor);
120
121
        /**
122
         * Lets index the assets folder.
123
         *
124
         */
125
        $scan->index($path);
126
127
128
        /**
129
         * Write a new tmp file so we can check if there where new or changed files found./
130
         */
131
        file_put_contents($tmpfile,'Hello world');
132
133
134
        /**
135
         * Modify one file..
136
         */
137
        file_put_contents($timefile, time());
138
139
140
        /**
141
         * Lets see if the scanner picked it up.
142
         */
143
        $report = $scan->scan();
144
145
        echo '<h1>New files</h1>';
146
        foreach($report->getNewfiles() as $file) {
147
            echo '<li>'.$file->getFilename().' '.$file->getMD5hash().'</li>';
148
        }
149
        echo '</ul>';
150
151
152
        echo '<h1>Modified Files</h1>';
153
        foreach($report->getModifiedFiles() as $file) {
154
            echo '<li>'.$file->getFilename().' '.$file->getMD5hash().'</li>';
155
        }
156
        echo '</ul>';
157
158
159
        file_put_contents($timefile, ''); /* Keep it empty */
160
        unlink($tmpfile);
161
162
       // $x = $databaseAdaptor->read();
163
       // print_r($x->getItems());
164
165
166
    } catch (Exception $e) {
167
        print '<pre>';
168
        print_r($e);
169
        print '</pre>';
170
    }
171
172
173
174
} else {
175
    die('This example requires mysqli to be loaded.');
176
}