Issues (219)

Branch: 4-cactus

BEdita/Core/src/Command/CustomPropsCommand.php (3 issues)

1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2021 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
namespace BEdita\Core\Command;
14
15
use Cake\Console\Arguments;
16
use Cake\Console\Command;
17
use Cake\Console\ConsoleIo;
18
use Cake\Console\ConsoleOptionParser;
19
use Cake\Core\Exception\Exception;
20
use Cake\ORM\Query;
21
use Cake\ORM\TableRegistry;
22
use Cake\Utility\Inflector;
23
use Generator;
24
25
/**
26
 * CustomProps command.
27
 *
28
 * Check custom properties formatting & validity
29
 *
30
 * @since 4.5.0
31
 */
32
class CustomPropsCommand extends Command
33
{
34
    /**
35
     * Table.
36
     *
37
     * @var \Cake\ORM\Table
38
     */
39
    protected $Table;
40
41
    /**
42
     * @inheritDoc
43
     */
44
    protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
45
    {
46
        return $parser->addOption('id', [
47
                'help' => 'Object ID to check',
48
                'required' => false,
49
            ])
50
            ->addOption('type', [
51
                'help' => 'Object type name to check',
52
                'short' => 't',
53
                'required' => false,
54
            ]);
55
    }
56
57
    /**
58
     * @inheritDoc
59
     */
60
    public function execute(Arguments $args, ConsoleIo $io): ?int
61
    {
62
        $types = TableRegistry::getTableLocator()->get('ObjectTypes')
63
            ->find('list', ['valueField' => 'name'])
64
            ->where(['is_abstract' => false])
65
            ->all()
66
            ->toList();
67
        if ($args->getOption('type')) {
68
            $types = [(string)$args->getOption('type')];
69
        }
70
        $errors = 0;
71
        foreach ($types as $type) {
72
            $errors += $this->customPropsByType($type, $args->getOption('id'), $io);
0 ignored issues
show
It seems like $args->getOption('id') can also be of type boolean and string; however, parameter $id of BEdita\Core\Command\Cust...nd::customPropsByType() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

72
            $errors += $this->customPropsByType($type, /** @scrutinizer ignore-type */ $args->getOption('id'), $io);
Loading history...
73
        }
74
        if ($errors) {
75
            $io->error(sprintf('Errors found (%d)', $errors));
76
77
            return self::CODE_ERROR;
78
        }
79
80
        $io->success('Done');
81
82
        return null;
83
    }
84
85
    /**
86
     * Check custom properties of an object type.
87
     *
88
     * @param string $type Object type
89
     * @param int|null $id Object ID
90
     * @param \Cake\Console\ConsoleIo $io Console IO
91
     * @return int Number of errors found
92
     */
93
    protected function customPropsByType(string $type, ?int $id, ConsoleIo $io): int
94
    {
95
        $io->info(sprintf('Processing %s...', $type));
96
        $this->Table = TableRegistry::getTableLocator()->get(Inflector::camelize($type));
97
        $query = $this->Table
98
            ->find('type', (array)$type);
99
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
100
            $query = $query->where(compact('id'));
101
        }
102
103
        $count = $err = 0;
104
        foreach ($this->objectsGenerator($query) as $object) {
105
            $props = (array)$object->get('custom_props');
106
            $object->set($props);
107
            try {
108
                $this->Table->saveOrFail($object);
0 ignored issues
show
$object of type array is incompatible with the type Cake\Datasource\EntityInterface expected by parameter $entity of Cake\ORM\Table::saveOrFail(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

108
                $this->Table->saveOrFail(/** @scrutinizer ignore-type */ $object);
Loading history...
109
                $count++;
110
            } catch (Exception $ex) {
111
                $msg = sprintf(
112
                    'Failed update on %s "%s" [id %d] - exception: %s',
113
                    $type,
114
                    $object->get('title'),
115
                    $object->id,
116
                    $ex->getMessage()
117
                );
118
                $this->log($msg, 'error');
119
                $err++;
120
            }
121
        }
122
        $io->success(sprintf('Updated %d %s without errors', $count, $type));
123
        if ($err) {
124
            $io->warning(sprintf('%d errors updating %s', $err, $type));
125
        }
126
127
        return $err;
128
    }
129
130
    /**
131
     * Objects generator.
132
     *
133
     * @param \Cake\ORM\Query $query Query object
134
     * @return \Generator
135
     */
136
    protected function objectsGenerator(Query $query): Generator
137
    {
138
        $pageSize = 1000;
139
        $pages = ceil($query->count() / $pageSize);
140
141
        for ($page = 1; $page <= $pages; $page++) {
142
            yield from $query
143
                ->page($page, $pageSize)
144
                ->toArray();
145
        }
146
    }
147
}
148