SitemapGeneratorCommand::execute()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 9.392
c 0
b 0
f 0
cc 3
nc 4
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
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 Sonata\SeoBundle\Command;
15
16
use Exception;
17
use Sonata\Exporter\Handler;
18
use Sonata\Exporter\Writer\SitemapWriter;
19
use Sonata\SeoBundle\Sitemap\SourceManager;
20
use Symfony\Component\Console\Command\Command;
21
use Symfony\Component\Console\Input\InputArgument;
22
use Symfony\Component\Console\Input\InputInterface;
23
use Symfony\Component\Console\Input\InputOption;
24
use Symfony\Component\Console\Output\OutputInterface;
25
use Symfony\Component\Filesystem\Filesystem;
26
use Symfony\Component\Finder\Finder;
27
use Symfony\Component\Routing\RequestContext;
28
use Symfony\Component\Routing\RouterInterface;
29
30
/**
31
 * Create a sitemap.
32
 *
33
 * @author Thomas Rabaix <[email protected]>
34
 */
35
final class SitemapGeneratorCommand extends Command
36
{
37
    /**
38
     * @var RouterInterface
39
     */
40
    private $router;
41
42
    /**
43
     * @var SourceManager
44
     */
45
    private $sitemapManager;
46
47
    /**
48
     * @var Filesystem
49
     */
50
    private $filesystem;
51
52
    public function __construct(
53
        ?RouterInterface $router,
54
        ?SourceManager $sitemapManager,
55
        ?Filesystem $filesystem
56
    ) {
57
        $this->router = $router;
58
        $this->sitemapManager = $sitemapManager;
59
        $this->filesystem = $filesystem;
60
61
        parent::__construct();
62
    }
63
64
    public function configure(): void
65
    {
66
        $this->setName('sonata:seo:sitemap');
67
68
        $this->addArgument('dir', InputArgument::REQUIRED, 'The directory to store the sitemap.xml file');
69
        $this->addArgument('host', InputArgument::REQUIRED, 'Set the host');
70
        $this->addOption('scheme', null, InputOption::VALUE_OPTIONAL, 'Set the scheme', 'http');
71
        $this->addOption('baseurl', null, InputOption::VALUE_OPTIONAL, 'Set the base url', '');
72
        $this->addOption(
73
            'sitemap_path',
74
            null,
75
            InputOption::VALUE_OPTIONAL,
76
            'Set the sitemap relative path (if in a specific directory)',
77
            ''
78
        );
79
80
        $this->setDescription('Create a sitemap');
81
        $this->setHelp(
82
            <<<'EOT'
83
The <info>sonata:seo:sitemap</info> command create new sitemap files (index + sitemap).
84
85
EOT
86
        );
87
    }
88
89
    public function execute(InputInterface $input, OutputInterface $output)
90
    {
91
        $host = $input->getArgument('host');
92
        $scheme = $input->getOption('scheme');
93
        $baseUrl = $input->getOption('baseurl');
94
        $permanentDir = $input->getArgument('dir');
95
        $appendPath = $input->hasOption('sitemap_path') ? $input->getOption('sitemap_path') : $baseUrl;
96
97
        $this->getContext()->setHost($host);
0 ignored issues
show
Bug introduced by
It seems like $host defined by $input->getArgument('host') on line 91 can also be of type array<integer,string> or null; however, Symfony\Component\Routin...questContext::setHost() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
98
        $this->getContext()->setScheme($scheme);
99
        $this->getContext()->setBaseUrl($baseUrl);
100
101
        $tempDir = $this->createTempDir($output);
102
        if (null === $tempDir) {
103
            $output->writeln('<error>The temporary directory already exists</error>');
104
            $output->writeln('<error>If the task is not running please delete this directory</error>');
105
106
            return 1;
107
        }
108
109
        $output->writeln(sprintf('Generating sitemap - this can take a while'));
110
        $this->generateSitemap($tempDir, $scheme, $host, $appendPath);
0 ignored issues
show
Bug introduced by
It seems like $host defined by $input->getArgument('host') on line 91 can also be of type array<integer,string> or null; however, Sonata\SeoBundle\Command...mand::generateSitemap() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
111
112
        $output->writeln(sprintf('Moving temporary file to %s ...', $permanentDir));
113
        $this->moveTemporaryFile($tempDir, $permanentDir);
0 ignored issues
show
Bug introduced by
It seems like $permanentDir defined by $input->getArgument('dir') on line 94 can also be of type array<integer,string> or null; however, Sonata\SeoBundle\Command...nd::moveTemporaryFile() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
114
115
        $output->writeln('Cleanup ...');
116
        $this->filesystem->remove($tempDir);
117
118
        $output->writeln('<info>done!</info>');
119
120
        return 0;
121
    }
122
123
    private function getContext(): RequestContext
124
    {
125
        return $this->router->getContext();
126
    }
127
128
    /**
129
     * Creates temporary directory if one does not exist.
130
     *
131
     * @return string|null Directory name or null if directory is already exist
132
     */
133
    private function createTempDir(OutputInterface $output): ?string
134
    {
135
        $tempDir = sys_get_temp_dir().'/sonata_sitemap_'.md5(__DIR__);
136
137
        $output->writeln(sprintf('Creating temporary directory: %s', $tempDir));
138
139
        if ($this->filesystem->exists($tempDir)) {
140
            return null;
141
        }
142
143
        $this->filesystem->mkdir($tempDir);
144
145
        return $tempDir;
146
    }
147
148
    /**
149
     * @throws \Exception
150
     */
151
    private function generateSitemap(string $dir, string $scheme, string $host, string $appendPath): void
152
    {
153
        foreach ($this->sitemapManager as $group => $sitemap) {
154
            $write = new SitemapWriter($dir, $group, $sitemap->types, false);
155
156
            try {
157
                Handler::create($sitemap->sources, $write)->export();
158
            } catch (Exception $e) {
159
                $this->filesystem->remove($dir);
160
161
                throw $e;
162
            }
163
        }
164
165
        // generate global sitemap index
166
        SitemapWriter::generateSitemapIndex(
167
            $dir,
168
            sprintf('%s://%s%s', $scheme, $host, $appendPath),
169
            'sitemap*.xml',
170
            'sitemap.xml'
171
        );
172
    }
173
174
    private function moveTemporaryFile(string $tempDir, string $permanentDir): void
175
    {
176
        $oldFiles = Finder::create()->files()->name('sitemap*.xml')->in($permanentDir);
177
        foreach ($oldFiles as $file) {
178
            $this->filesystem->remove($file->getRealPath());
179
        }
180
181
        $newFiles = Finder::create()->files()->name('sitemap*.xml')->in($tempDir);
182
        foreach ($newFiles as $file) {
183
            $this->filesystem->rename($file->getRealPath(), sprintf('%s/%s', $permanentDir, $file->getFilename()));
184
        }
185
    }
186
}
187