Test Setup Failed
Push — master ( 8f0691...0a826f )
by Craig
05:50
created

configureActivatedStatus()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula Foundation - https://ziku.la/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Zikula\Bundle\CoreBundle\Command;
15
16
use Doctrine\DBAL\Connection;
17
use Doctrine\DBAL\DBALException;
18
use Symfony\Component\Console\Command\Command;
19
use Symfony\Component\Console\Input\InputArgument;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\InputOption;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Style\SymfonyStyle;
24
use Zikula\UsersModule\Constant as UsersConstant;
25
use Zikula\ZAuthModule\ZAuthConstant;
26
27
class GenerateTestUsersCommand extends Command
28
{
29
    /**
30
     * @var Connection
31
     */
32
    private $conn;
33
34
    /**
35
     * @var \DateTime
36
     */
37
    private $nowUTC;
38
39
    /**
40
     * @var \DateTime
41
     */
42
    private $startUTC;
43
44
    /**
45
     * @var \DateTime
46
     */
47
    private $regDate;
48
49
    /**
50
     * @var bool
51
     */
52
    private $range = false;
53
54
    /**
55
     * @var string
56
     */
57
    private $active;
58
59
    /**
60
     * @var int
61
     */
62
    private $verified;
63
64
    protected static $defaultName = 'zikula:users:generate';
65
66
    public function __construct(
67
        Connection $connection
68
    ) {
69
        parent::__construct();
70
        $this->conn = $connection;
71
        $utcTZ = new \DateTimeZone('UTC');
72
        $this->nowUTC = new \DateTime('now', $utcTZ);
73
        $this->startUTC = new \DateTime('1970-01-01 00:00:00', $utcTZ);
74
    }
75
76
    protected function configure()
77
    {
78
        $this
79
            ->setDefinition([
80
                new InputArgument('amount', InputArgument::REQUIRED, 'The number of users to create'),
81
            ])
82
            ->addOption(
83
                'active',
84
                null,
85
                InputOption::VALUE_REQUIRED,
86
                'All the users are A=active, I=inactive, P=all users pending, M=all users marked for deletion, R=random choice A|I|P|M',
87
                'A'
88
            )
89
            ->addOption(
90
                'verified',
91
                null,
92
                InputOption::VALUE_REQUIRED,
93
                'All the user emails marked as 1=verified, 0=unverified, 2=random choice 0|1',
94
                1
95
            )
96
            ->addOption(
97
                'regdate',
98
                null,
99
                InputOption::VALUE_REQUIRED,
100
                'Registration date in format YYYYMMDD or >YYYYMMDD if random dates between provided date and now'
101
            )
102
            ->setDescription('Generates users for testing purposes')
103
            ->setHelp(
104
                <<<'EOT'
105
The <info>%command.name%</info> command generates users in order to fill a database for testing purposes.
106
These users will not be able to login. The users are placed into a newly created group, not the standard users group.
107
108
<info>php %command.full_name% 1000</info>
109
110
This will generate 1000 randomly named users using all the default values.
111
112
Options:
113
<info>--active</info> A|I|P|M|R (default: A) A=all users active, I=all users inactive, P=all users pending, M=all users marked for deletion R=random assignment A|I|P|M
114
115
<info>--verified</info> 0|1|2 (default: 1) 1=all user emails verified, 0=all user emails unverified, 2=random assignment 0|1
116
117
<info>--regdate</info> YYYYMMDD or >YYYYMMDD if random dates between provided date and now
118
119
<info>php %command.full_name% 1000 --active=I --verified=2 --regdate='>20200101'</info>
120
121
EOT
122
            );
123
    }
124
125
    protected function execute(InputInterface $input, OutputInterface $output): int
126
    {
127
        $io = new SymfonyStyle($input, $output);
128
        $amount = (int) abs($input->getArgument('amount'));
129
        $key = bin2hex(random_bytes(3));
130
        $groupId = $this->createGroup($key);
131
        $divisor = (int) ceil($amount / 100);
132
        $this->active = (string) $input->getOption('active');
133
        $this->verified = in_array((int) $input->getOption('verified'), [0, 1, 2]) ? (int) $input->getOption('verified') : 1;
134
        $regDate = $input->getOption('regdate') ?? $this->nowUTC;
135
        $this->range = '>' === $regDate[0];
136
        $this->regDate = $this->range ? mb_substr($regDate, 1) : $regDate;
137
138
        $io->title('User generation utility');
139
        $io->text('Generating users...');
140
        $io->progressStart($amount);
141
142
        for ($i = 1; $i <= $amount; $i++) {
143
            $uname = 'user' . $key . $i;
144
            $this->insertUser($uname);
145
            $uid = (int) $this->conn->lastInsertId();
146
            $this->insertAttributes($uid);
147
            $this->insertMapping($uid, $uname);
148
            $this->insertGroup($uid, $groupId);
149
            if (0 === $i % $divisor) {
150
                $io->progressAdvance((int) ceil($amount / $divisor));
151
            }
152
        }
153
154
        $io->progressFinish();
155
        $io->success('User generation complete!');
156
        $io->text(sprintf('%d users created (group name: <info>group%s</info>).', $amount, $key));
157
158
        return 0;
159
    }
160
161
    private function configureActivatedStatus(string $value): int {
162
        $statuses = [
163
            'A' => UsersConstant::ACTIVATED_ACTIVE,
164
            'I' => UsersConstant::ACTIVATED_INACTIVE,
165
            'P' => UsersConstant::ACTIVATED_PENDING_REG,
166
            'M' => UsersConstant::ACTIVATED_PENDING_DELETE
167
        ];
168
        if ('R' === $value) {
169
            return array_rand(array_flip($statuses));
170
        }
171
172
        return array_key_exists($value, $statuses) ? $statuses[$value] : UsersConstant::ACTIVATED_ACTIVE;
173
    }
174
175
    private function configureRegDate(): \DateTime
176
    {
177
        if ($this->regDate instanceof \DateTime) {
0 ignored issues
show
introduced by
$this->regDate is always a sub-type of DateTime.
Loading history...
178
            return $this->regDate;
179
        }
180
        $utcTz = new \DateTimeZone('UTC');
181
        $regDate = \DateTime::createFromFormat('Ymd', $this->regDate, $utcTz);
182
        if (!$this->range) {
183
            return $regDate;
184
        }
185
        $randTimeStamp = mt_rand($regDate->getTimestamp(), $this->nowUTC->getTimestamp());
186
187
        return \DateTime::createFromFormat("U", "$randTimeStamp", $utcTz);
188
    }
189
190
    private function insertUser(string $uname): void
191
    {
192
        $types = [\PDO::PARAM_STR, \PDO::PARAM_STR, \PDO::PARAM_INT, 'datetime', \PDO::PARAM_INT, 'datetime', 'datetime', \PDO::PARAM_STR, \PDO::PARAM_STR];
193
        try {
194
            $this->conn->insert('users', [
195
                'uname' => $uname,
196
                'email' => $uname . '@example.com',
197
                'activated' => $activated = $this->configureActivatedStatus($this->active),
198
                'approved_date' => UsersConstant::ACTIVATED_ACTIVE === $activated ? $this->nowUTC : $this->startUTC,
199
                'approved_by' => UsersConstant::ACTIVATED_ACTIVE === $activated ? 2 : 0,
200
                'user_regdate' => $this->configureRegDate(),
201
                'lastlogin' => $this->startUTC,
202
                'tz' => '',
203
                'locale' => ''
204
            ], $types);
205
        } catch (DBALException $exception) {
206
            // do nothing?
207
        }
208
    }
209
210
    private function insertAttributes(int $uid): void
211
    {
212
        $types = [\PDO::PARAM_STR, \PDO::PARAM_INT, \PDO::PARAM_STR];
213
        try {
214
            $this->conn->insert('users_attributes', [
215
                'name' => UsersConstant::AUTHENTICATION_METHOD_ATTRIBUTE_KEY,
216
                'user_id' => $uid,
217
                'value' => ZAuthConstant::AUTHENTICATION_METHOD_EITHER,
218
            ], $types);
219
        } catch (DBALException $exception) {
220
            // do nothing?
221
        }
222
    }
223
224
    private function insertMapping(int $uid, string $uname): void
225
    {
226
        $types = [\PDO::PARAM_STR, \PDO::PARAM_INT, \PDO::PARAM_STR, \PDO::PARAM_STR, \PDO::PARAM_INT, \PDO::PARAM_STR];
227
        try {
228
            $this->conn->insert('zauth_authentication_mapping', [
229
                'method' => ZAuthConstant::AUTHENTICATION_METHOD_EITHER,
230
                'uid' => $uid,
231
                'uname' => $uname,
232
                'email' => $uname . '@example.com',
233
                'verifiedEmail' => 2 === $this->verified ? random_int(0, 1) : $this->verified,
234
                'pass' => '',
235
            ], $types);
236
        } catch (DBALException $exception) {
237
            // do nothing?
238
        }
239
    }
240
241
    private function createGroup(string $key): int
242
    {
243
        $types = [\PDO::PARAM_STR, \PDO::PARAM_INT, \PDO::PARAM_STR, \PDO::PARAM_INT, \PDO::PARAM_INT];
244
        try {
245
            $this->conn->insert('groups', [
246
                'name' => 'group' . $key,
247
                'gtype' => 0,
248
                'description' => 'temp group for testing',
249
                'state' => 0,
250
                'nbumax' => 0,
251
            ], $types);
252
        } catch (DBALException $exception) {
253
            // do nothing?
254
        }
255
256
        return (int) $this->conn->lastInsertId();
257
    }
258
259
    private function insertGroup(int $uid, int $gid): void
260
    {
261
        $types = [\PDO::PARAM_INT, \PDO::PARAM_INT];
262
        try {
263
            $this->conn->insert('group_membership', [
264
                'uid' => $uid,
265
                'gid' => $gid,
266
            ], $types);
267
        } catch (DBALException $exception) {
268
            // do nothing?
269
        }
270
    }
271
}
272