Completed
Push — master ( 3205ef...24db78 )
by Vitaly
03:36
created

CMS::afterCompress()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12
Metric Value
dl 0
loc 12
ccs 0
cts 7
cp 0
rs 9.4285
cc 3
eloc 5
nc 3
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 module cache folder if not exists
200
        if (!file_exists($this->cache_path)) {
201
            @mkdir($this->cache_path, 0777, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

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