SurahCommand::configure()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 20
nc 1
nop 0
dl 0
loc 25
ccs 0
cts 0
cp 0
crap 2
rs 9.6
c 1
b 0
f 0
1
<?php
2
3
namespace FaizShukri\Quran\Commands;
4
5
use FaizShukri\Quran\Exceptions\SurahInvalid;
6
use FaizShukri\Quran\Quran;
7
use FaizShukri\Quran\Supports\Levenshtein;
8
use Symfony\Component\Console\Command\Command;
9
use Symfony\Component\Console\Helper\HelperSet;
10
use Symfony\Component\Console\Helper\QuestionHelper;
11
use Symfony\Component\Console\Helper\Table;
12
use Symfony\Component\Console\Helper\TableCell;
13
use Symfony\Component\Console\Input\InputArgument;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use Symfony\Component\Console\Question\ChoiceQuestion;
17
18
class SurahCommand extends Command
19
{
20
    private $quran;
21
22
    /**
23
     * @codeCoverageIgnore
24
     */
25
    public function __construct()
26
    {
27
        parent::__construct();
28
        $this->quran = new Quran();
29
    }
30
31
    /**
32
     * @codeCoverageIgnore
33
     */
34
    protected function configure()
35
    {
36
        $this
37
            ->setName('surah')
38
            ->setDescription('Retrieve surah')
39
            ->addArgument(
40
                'surah',
41
                InputArgument::OPTIONAL,
42
                'Specify surah'
43
            )
44
            ->addArgument(
45
                'ayah',
46
                InputArgument::OPTIONAL,
47
                'Specify ayah'
48
            )
49
            ->addArgument(
50
                'translation',
51
                InputArgument::OPTIONAL,
52
                'Specify ayah'
53
            )
54
            ->addUsage('2')
55
            ->addUsage('2 3')
56
            ->addUsage('2 3,5 en')
57
            ->addUsage('baqara 3-5')
58
            ->addUsage('baqara 3,5-6 ar,en')
59
        ;
60
    }
61
62 22
    protected function execute(InputInterface $input, OutputInterface $output): int
63
    {
64 22
        $surah = $input->getArgument('surah');
65 22
        $ayah = $input->getArgument('ayah');
66 22
        $translation = $input->getArgument('translation');
67
68 22
        $surah = $this->confirmSurahNo($surah, $input, $output);
0 ignored issues
show
Bug introduced by
It seems like $surah can also be of type string[]; however, parameter $surah of FaizShukri\Quran\Command...mmand::confirmSurahNo() does only seem to accept integer|null|string, 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

68
        $surah = $this->confirmSurahNo(/** @scrutinizer ignore-type */ $surah, $input, $output);
Loading history...
69
70 20
        if ($ayah) {
71 12
            if ($translation) {
72 10
                $this->quran->translation($translation);
73
            }
74
75 12
            $ayah = $this->quran->get($surah.':'.$ayah);
0 ignored issues
show
Bug introduced by
Are you sure $ayah of type string|string[] can be used in concatenation? ( Ignorable by Annotation )

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

75
            $ayah = $this->quran->get($surah.':'./** @scrutinizer ignore-type */ $ayah);
Loading history...
76 12
            $output->writeln($this->parseResult($ayah));
77 8
        } elseif ($surah) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $surah 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...
78 6
            $this->chapter($output, $surah);
79
        } else {
80 2
            $this->chapters($output);
81
        }
82
83 20
        return 0;
84
    }
85
86 6
    private function chapter($output, $verse)
87
    {
88 6
        $surah = $this->quran->getSource()->surah($verse);
89
90 6
        $table = new Table($output);
91 3
        $table
92 6
            ->setHeaders([
93 6
                [new TableCell('Surah '.$surah['tname'], array('colspan' => 2))],
94 3
            ])
95 6
            ->setRows([
96 6
                ['Index',  $surah['index']],
97 6
                ['Name',  $surah['tname']],
98 6
                ['Name (ar)',  $surah['name']],
99 6
                ['Meaning',  $surah['ename']],
100 6
                ['No. Ayah',  $surah['ayas']],
101 6
                ['Start',  $surah['start']],
102 6
                ['Type',  $surah['type']],
103 6
                ['Order',  $surah['order']],
104 6
                ['Rukus',  $surah['rukus']],
105 3
            ])
106 6
            ->setStyle('borderless')
107 3
        ;
108 6
        $table->render();
109 6
        $output->writeln("");
110 3
    }
111
112 2
    private function chapters($output)
113
    {
114 2
        $surah = $this->quran->getSource()->surah();
115 1
        $surah = array_map(function ($sura) { return "$sura->index. $sura->tname"; }, $surah);
116 2
        $surah = $this->array_chunk_vertical($surah, 4);
117
118 2
        $table = new Table($output);
119 1
        $table
120 2
            ->setHeaders([
121 2
                [new TableCell('All surah', array('colspan' => 4))],
122 1
            ])
123 2
            ->setRows($surah)
124 1
        ;
125 2
        $table->render();
126 1
    }
127
128 2
    private function array_chunk_vertical($data, $columns)
129
    {
130 2
        $n = count($data);
131 2
        $per_column = floor($n / $columns);
132 2
        $rest = $n % $columns;
133
134
        // The map
135 2
        $per_columns = array();
136 2
        for ($i = 0; $i < $columns; ++$i) {
137 2
            $per_columns[$i] = $per_column + ($i < $rest ? 1 : 0);
138
        }
139
140 2
        $tabular = array();
141 2
        foreach ($per_columns as $rows) {
142 2
            for ($i = 0; $i < $rows; ++$i) {
143 2
                $tabular[$i][ ] = array_shift($data);
144
            }
145
        }
146
147 2
        return $tabular;
148
    }
149
150
    /**
151
     * @param int|string|null $surah
152
     * @param InputInterface  $input
153
     * @param OutputInterface $output
154
     *
155
     * @return int|null
156
     *
157
     * @throws SurahInvalid
158
     */
159 22
    private function confirmSurahNo($surah, $input, $output)
160
    {
161 22
        if ($surah && !is_numeric($surah)) {
162 8
            $surah_list = $this->quran->surah();
163 4
            $surah_list_array = array_map(function ($surah) { return $surah->tname; }, (array) $surah_list);
164 8
            $closest_surah = $this->closestSurah($surah, $surah_list_array);
165
166 8
            if (sizeof($closest_surah) == 1) {
167 4
                $surah = $closest_surah[0];
168 4
            } elseif (sizeof($closest_surah) > 1) {
169 2
                $surah = $this->askSurah($closest_surah, $input, $output);
170
            } else {
171 2
                throw new SurahInvalid();
172
            }
173 6
            $surah = array_search($surah, $surah_list_array);
174
        }
175
176 20
        return ($surah === null) ? null : intval($surah);
177
    }
178
179
    /**
180
     * @param string $surah
181
     * @param array  $options
182
     *
183
     * @return array
184
     */
185 8
    private function closestSurah($surah, $options)
186
    {
187 8
        $l = new Levenshtein();
188
189 8
        return $l->closest($surah, $options);
190
    }
191
192
    /**
193
     * @param array           $options
194
     * @param InputInterface  $input
195
     * @param OutputInterface $output
196
     *
197
     * @return mixed
198
     */
199 2
    private function askSurah($options, $input, $output)
200
    {
201 2
        $this->setHelperSet(new HelperSet([new QuestionHelper()]));
202 2
        $helper = $this->getHelper('question');
203 2
        $question = new ChoiceQuestion('No surah found. Did you mean one of the following?', $options, 0);
204 2
        $question->setErrorMessage('Surah %s is invalid.');
205
206 2
        return $helper->ask($input, $output, $question);
0 ignored issues
show
Bug introduced by
The method ask() does not exist on Symfony\Component\Console\Helper\Helper. It seems like you code against a sub-type of Symfony\Component\Console\Helper\Helper such as Symfony\Component\Console\Helper\QuestionHelper. ( Ignorable by Annotation )

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

206
        return $helper->/** @scrutinizer ignore-call */ ask($input, $output, $question);
Loading history...
207
    }
208
209 12
    private function parseResult($args)
210
    {
211
        // Just a single ayah is return. No need to parse anything.
212 12
        if (is_string($args)) {
213 6
            return $args."\n";
214
        }
215
216
        // Multiple ayah/one surah or multiple surah/one ayah. Not both.
217 6
        if (is_string(current($args))) {
218 4
            return $this->buildAyah($args);
219
        }
220
221
        // Both multiple ayah and multiple surah.
222 2
        $count = 0;
223 2
        $result = "\n";
224
225 2
        foreach ($args as $translation => $aya) {
226 2
            $result .= strtoupper($translation)."\n".str_repeat('=', strlen($translation) + 2)."\n\n";
227 2
            $result .= $this->buildAyah($aya);
228
229 2
            ++$count;
230 2
            if ($count < sizeof($args)) {
231 2
                $result .= "\n\n";
232
            }
233
        }
234
235 2
        return $result;
236
    }
237
238 6
    private function buildAyah($ayah)
239
    {
240 6
        $result = '';
241 6
        foreach ($ayah as $num => $aya) {
242 6
            $result .= '[ '.strtoupper($num)." ]\t".$aya."\n";
243
        }
244
245 6
        return $result;
246
    }
247
}
248