Completed
Push — master ( 4f4aae...6745e3 )
by Vitaly
02:58
created

CMS::readSQL()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 16
ccs 0
cts 7
cp 0
rs 9.4285
cc 3
eloc 6
nc 2
nop 2
crap 12
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 5.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
namespace samsoncms\api;
3
4
// Backward compatibility
5 1
require('generated/Material.php');
6 1
require('generated/Field.php');
7 1
require('generated/MaterialField.php');
8 1
require('generated/Structure.php');
9 1
require('generated/StructureField.php');
10
11
use samsoncms\api\generator\analyzer\Virtual;
12
use samsoncms\api\generator\Collection;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, samsoncms\api\Collection.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
13
use samsoncms\api\generator\Entity;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, samsoncms\api\Entity.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
14
use samsoncms\api\generator\Gallery;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, samsoncms\api\Gallery.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
use samsoncms\api\generator\Metadata;
16
use samsoncms\api\generator\Query;
17
use samsoncms\api\generator\Analyzer;
18
19
use samsoncms\application\GeneratorApplication;
20
use samsonframework\core\ResourcesInterface;
21
use samsonframework\core\SystemInterface;
22
use samson\activerecord\TableRelation;
23
use samson\core\CompressableService;
24
use samson\activerecord\dbMySQLConnector;
25
use samsonphp\generator\Generator;
26
27
/**
28
 * SamsonCMS API
29
 * @package samsoncms\api
30
 */
31
class CMS extends CompressableService
0 ignored issues
show
Deprecated Code introduced by
The class samson\core\CompressableService has been deprecated with message: Just implement samsonframework\core\CompressInterface

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
32
{
33
    /** Database entity name for relations between material and navigation */
34
    const MATERIAL_NAVIGATION_RELATION_ENTITY = '\samson\activerecord\structurematerial';
35
    /** Database entity name for relations between material and images */
36
    const MATERIAL_IMAGES_RELATION_ENTITY = GalleryField::class;
37
    /** Database entity name for relations between additional fields and navigation */
38
    const FIELD_NAVIGATION_RELATION_ENTITY = '\samson\activerecord\structurefield';
39
    /** Database entity name for relations between material and additional fields values */
40
    const MATERIAL_FIELD_RELATION_ENTITY = MaterialField::class;
41
42
    /** Identifier */
43
    protected $id = 'cmsapi2';
44
45
    /** @var \samsonframework\orm\DatabaseInterface */
46
    protected $database;
47
48
    /** @var array[string] Collection of generated queries */
49
    protected $queries;
50
51
    /** @var string Database table names prefix */
52
    public $tablePrefix = '';
53
54
    /**
55
     * CMS constructor.
56
     *
57
     * @param string $path
58
     * @param ResourcesInterface $resources
59
     * @param SystemInterface $system
60
     */
61
    public function  __construct($path, ResourcesInterface $resources, SystemInterface $system)
0 ignored issues
show
Coding Style introduced by
Expected "function abc(...)"; found "function abc(...)"
Loading history...
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 2 found
Loading history...
62
    {
63
        // TODO: This should changed to normal DI
64
        $this->database = db();
65
66
        parent::__construct($path, $resources, $system);
67
    }
68
69
    /**
70
     * Module initialization.
71
     *
72
     * @param array $params Initialization parameters
73
     * @return boolean|null Initialization result
74
     */
75
    public function init(array $params = array())
76
    {
77
        $this->rewriteEntityLocale();
78
    }
79
80
    public function beforeCompress(& $obj = null, array & $code = null)
81
    {
82
83
    }
84
85
    public function afterCompress(& $obj = null, array & $code = null)
86
    {
87
        // Iterate through generated php code
88
        $files = array();
89
        foreach (\samson\core\File::dir($this->cache_path, 'php', '', $files, 1) as $file) {
90
            // No namespace for global function file
91
            $ns = strpos($file, 'func') === false ? __NAMESPACE__ : '';
92
93
            // Compress generated php code
94
            $obj->compress_php($file, $this, $code, $ns);
95
        }
96
    }
97
98
    /**
99
     * Entity additional fields localization support.
100
     */
101
    protected function rewriteEntityLocale()
102
    {
103
        // Iterate all generated entity classes
104
        foreach (get_declared_classes() as $entityClass) {
105
            if (is_subclass_of($entityClass, '\samsoncms\api\Entity')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
106
                // Insert current application locale
107
                str_replace('@locale', locale(), $entityClass::$_sql_select);
108
            }
109
        }
110
    }
111
112
    //[PHPCOMPRESSOR(remove,start)]
113
    /**
114
     * Read SQL file with variables placeholders pasting
115
     * @param string $filePath SQL file for reading
116
     * @param string $prefix Prefix for addition
117
     * @return array Collection of SQL command texts
118
     */
119
    public function readSQL($filePath, $prefix = '')
120
    {
121
        $sql = '';
122
123
        // Build path to SQL folder
124
        if (file_exists($filePath)) {
125
            // Replace prefix
126
            $sql = str_replace('@prefix', $prefix, file_get_contents($filePath));
127
        }
128
129
        // Split queries
130
        $sqlCommands = explode(';', str_replace("\n", '', $sql));
131
132
        // Always return array
133
        return array_filter(is_array($sqlCommands) ? $sqlCommands : array($sqlCommands));
134
    }
135
136
    /**
137
     * @see ModuleConnector::prepare()
138
     */
139
    public function prepare()
140
    {
141
        // Create cms_version
142
        $this->database->execute('
143
CREATE TABLE IF NOT EXISTS `cms_version`  (
144
  `version` varchar(15) NOT NULL DEFAULT \'30\'
145
) ENGINE=InnoDB DEFAULT CHARSET=utf8;'
146
        );
147
148
        // Perform this migration and execute only once
149
        if ($this->migrator() != 40) {
150
            // Perform SQL table creation
151
            $path = __DIR__ . '/../sql/';
152
            foreach (array_slice(scandir($path), 2) as $file) {
153
                trace('Performing database script ['.$file.']');
154
                foreach ($this->readSQL($path . $file, $this->tablePrefix) as $sql) {
155
                    $this->database->execute($sql);
156
                }
157
            }
158
            $this->migrator(40);
159
        }
160
161
        // Initiate migration mechanism
162
        $this->database->migration(get_class($this), array($this, 'migrator'));
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface samsonframework\orm\DatabaseInterface as the method migration() does only exist in the following implementations of said interface: samson\activerecord\dbMySQL.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
163
164
                // Define permanent table relations
165
        new TableRelation('material', 'user', 'UserID', 0, 'user_id');
166
        new TableRelation('material', 'gallery', 'MaterialID', TableRelation::T_ONE_TO_MANY);
167
        new TableRelation('material', 'materialfield', 'MaterialID', TableRelation::T_ONE_TO_MANY);
168
        new TableRelation('material', 'field', 'materialfield.FieldID', TableRelation::T_ONE_TO_MANY);
169
        new TableRelation('material', 'structurematerial', 'MaterialID', TableRelation::T_ONE_TO_MANY);
170
        new TableRelation('material', 'structure', 'structurematerial.StructureID', TableRelation::T_ONE_TO_MANY);
171
        new TableRelation('materialfield', 'field', 'FieldID');
172
        new TableRelation('materialfield', 'material', 'MaterialID');
173
        new TableRelation('structurematerial', 'structure', 'StructureID');
174
        new TableRelation('structurematerial', 'materialfield', 'MaterialID', TableRelation::T_ONE_TO_MANY);
175
        new TableRelation('structurematerial', 'material', 'MaterialID', TableRelation::T_ONE_TO_MANY);
176
        new TableRelation('structure', 'material', 'structurematerial.MaterialID', TableRelation::T_ONE_TO_MANY, null, 'manymaterials');
177
        new TableRelation('structure', 'gallery', 'structurematerial.MaterialID', TableRelation::T_ONE_TO_MANY, null, 'manymaterials');
178
        /*new TableRelation( 'structure', 'material', 'MaterialID' );*/
179
        new TableRelation('structure', 'user', 'UserID', 0, 'user_id');
180
        new TableRelation('structure', 'materialfield', 'material.MaterialID', TableRelation::T_ONE_TO_MANY, 'MaterialID', '_mf');
181
        new TableRelation('structure', 'structurematerial', 'StructureID', TableRelation::T_ONE_TO_MANY);
182
        //new TableRelation('related_materials', 'material', 'first_material', TableRelation::T_ONE_TO_MANY, 'MaterialID');
183
        //new TableRelation('related_materials', 'materialfield', 'first_material', TableRelation::T_ONE_TO_MANY, 'MaterialID');
184
        new TableRelation('field', 'structurefield', 'FieldID');
185
        new TableRelation('field', 'structure', 'structurefield.StructureID');
186
        new TableRelation('structurefield', 'field', 'FieldID');
187
        new TableRelation('structurefield', 'materialfield', 'FieldID');
188
        new TableRelation('structurefield', 'material', 'materialfield.MaterialID');
189
        new TableRelation('structure', 'structure_relation', 'StructureID', TableRelation::T_ONE_TO_MANY, 'parent_id', 'children_relations');
190
        new TableRelation('structure', 'structure', 'children_relations.child_id', TableRelation::T_ONE_TO_MANY, 'StructureID', 'children');
191
        new TableRelation('structure', 'structure_relation', 'StructureID', TableRelation::T_ONE_TO_MANY, 'child_id', 'parents_relations');
192
        new TableRelation('structure', 'structure', 'parents_relations.parent_id', TableRelation::T_ONE_TO_MANY, 'StructureID', 'parents');
193
        new TableRelation('structurematerial', 'structure_relation', 'StructureID', TableRelation::T_ONE_TO_MANY, 'parent_id');
194
        new TableRelation('groupright', 'right', 'RightID', TableRelation::T_ONE_TO_MANY);
195
196
        // TODO: Should be removed
197
        m('activerecord')->relations();
0 ignored issues
show
Deprecated Code introduced by
The function m() has been deprecated with message: Use $this->system->module() in module context

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
198
199
        // Create database analyzer
200
        $generator = new Virtual($this->database);
201
        // Analyze database structure and get entities metadata
202
        foreach ($generator->analyze() as $metadata) {
203
            // Create entity generated class names
204
            $entityFile = $this->cache_path.$metadata->entity.'.php';
205
            $queryFile = $this->cache_path.$metadata->entity.'Query.php';
206
            $collectionFile = $this->cache_path.$metadata->entity.'Collection.php';
207
208
            // Create entity query class generator
209
            $entityGenerator = new Entity(new Generator(__NAMESPACE__.'\\generated'), $metadata);
210
            // Create entity query class generator
211
            $queryGenerator = new Query(new Generator(__NAMESPACE__.'\\generated'), $metadata);
212
            // Create entity query collection class generator
213
            $collectionGenerator = new Collection(new Generator(__NAMESPACE__.'\\generated'), $metadata);
214
215
            // Create entity query class files
216
            file_put_contents($entityFile, '<?php' . $entityGenerator->generate());
217
            file_put_contents($queryFile, '<?php' . $queryGenerator->generate());
218
            file_put_contents($collectionFile, '<?php' . $collectionGenerator->generate());
219
220
            // Require files
221
            require($entityFile);
222
            require($queryFile);
223
            require($collectionFile);
224
        }
225
226
        // Create database analyzer
227
        $generator = new \samsoncms\api\generator\analyzer\Gallery($this->database);
228
        // Analyze database structure and get entities metadata
229
        foreach ($generator->analyze() as $metadata) {
230
            // Create entity generated class names
231
            $entityFile = $this->cache_path.$metadata->entity.'.php';
232
233
            // Create entity query class generator
234
            $entityGenerator = new Gallery(new Generator(__NAMESPACE__.'\\generated'), $metadata);
235
236
            // Create entity query class files
237
            file_put_contents($entityFile, '<?php' . $entityGenerator->generate());
238
239
            // Require files
240
            require($entityFile);
241
        }
242
243
//        // Generate entities classes file
244
//        $generatorApi = new GeneratorApi($this->database);
245
//        //$queryGenerator = new Query($this->database);
246
//
247
//        // Create cache file
248
//        $file = md5($generatorApi->entityHash()).'.php';
249
//        if ($this->cache_refresh($file)) {
250
//            file_put_contents($file, '<?php ' . $generatorApi->createEntityClasses());
251
//        }
252
//
253
//        // Include entities file
254
//        require($file);
255
256
        return parent::prepare();
257
    }
258
259
    /**
260
     * Handler for CMSAPI database version manipulating
261
     * @param string $toVersion Version to switch to
262
     * @return string Current database version
263
     */
264
    public function migrator($toVersion = null)
265
    {
266
        // If something passed - change database version to it
267
        if (func_num_args()) {
268
            // Save current version to special db table
269
            $this->database->execute(
270
                "ALTER TABLE  `" . dbMySQLConnector::$prefix . "cms_version`
271
                CHANGE  `version`  `version` VARCHAR( 15 ) CHARACTER SET utf8
272
                COLLATE utf8_general_ci NOT NULL DEFAULT  '" . $toVersion . "';"
273
            );
274
            die('Database successfully migrated to [' . $toVersion . ']');
275
        } else { // Return current database version
276
            $version_row = $this->database->fetch('SHOW COLUMNS FROM `' . dbMySQLConnector::$prefix . 'cms_version`');
277
            if (isset($version_row[0]['Default'])) {
278
                return $version_row[0]['Default'];
279
            } else {
280
                return 0;
281
            }
282
        }
283
    }
284
    //[PHPCOMPRESSOR(remove,end)]
285
}
286