Completed
Pull Request — 2.x (#368)
by Grégoire
01:27
created

SitemapGeneratorCommand::setContainer()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
cc 2
nc 2
nop 1
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(<<<'EOT'
82
The <info>sonata:seo:sitemap</info> command create new sitemap files (index + sitemap).
83
84
EOT
85
        );
86
    }
87
88
    public function execute(InputInterface $input, OutputInterface $output)
89
    {
90
        $host = $input->getArgument('host');
91
        $scheme = $input->getOption('scheme');
92
        $baseUrl = $input->getOption('baseurl');
93
        $permanentDir = $input->getArgument('dir');
94
        $appendPath = $input->hasOption('sitemap_path') ? $input->getOption('sitemap_path') : $baseUrl;
95
96
        $this->getContext()->setHost($host);
0 ignored issues
show
Bug introduced by
It seems like $host defined by $input->getArgument('host') on line 90 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...
97
        $this->getContext()->setScheme($scheme);
98
        $this->getContext()->setBaseUrl($baseUrl);
99
100
        $tempDir = $this->createTempDir($output);
101
        if (null === $tempDir) {
102
            $output->writeln('<error>The temporary directory already exists</error>');
103
            $output->writeln('<error>If the task is not running please delete this directory</error>');
104
105
            return 1;
106
        }
107
108
        $output->writeln(sprintf('Generating sitemap - this can take a while'));
109
        $this->generateSitemap($tempDir, $scheme, $host, $appendPath);
0 ignored issues
show
Bug introduced by
It seems like $host defined by $input->getArgument('host') on line 90 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...
110
111
        $output->writeln(sprintf('Moving temporary file to %s ...', $permanentDir));
112
        $this->moveTemporaryFile($tempDir, $permanentDir);
0 ignored issues
show
Bug introduced by
It seems like $permanentDir defined by $input->getArgument('dir') on line 93 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...
113
114
        $output->writeln('Cleanup ...');
115
        $this->filesystem->remove($tempDir);
116
117
        $output->writeln('<info>done!</info>');
118
119
        return 0;
120
    }
121
122
    private function getContext(): RequestContext
123
    {
124
        return $this->router->getContext();
125
    }
126
127
    /**
128
     * Creates temporary directory if one does not exist.
129
     *
130
     * @return string|null Directory name or null if directory is already exist
131
     */
132
    private function createTempDir(OutputInterface $output): ?string
133
    {
134
        $tempDir = sys_get_temp_dir().'/sonata_sitemap_'.md5(__DIR__);
135
136
        $output->writeln(sprintf('Creating temporary directory: %s', $tempDir));
137
138
        if ($this->filesystem->exists($tempDir)) {
139
            return null;
140
        }
141
142
        $this->filesystem->mkdir($tempDir);
143
144
        return $tempDir;
145
    }
146
147
    /**
148
     * @throws \Exception
149
     */
150
    private function generateSitemap(string $dir, string $scheme, string $host, string $appendPath): void
151
    {
152
        foreach ($this->sitemapManager as $group => $sitemap) {
153
            $write = new SitemapWriter($dir, $group, $sitemap->types, false);
154
155
            try {
156
                Handler::create($sitemap->sources, $write)->export();
157
            } catch (Exception $e) {
158
                $this->filesystem->remove($dir);
159
160
                throw $e;
161
            }
162
        }
163
164
        // generate global sitemap index
165
        SitemapWriter::generateSitemapIndex(
166
            $dir,
167
            sprintf('%s://%s%s', $scheme, $host, $appendPath),
168
            'sitemap*.xml',
169
            'sitemap.xml'
170
        );
171
    }
172
173
    private function moveTemporaryFile(string $tempDir, string $permanentDir): void
174
    {
175
        $oldFiles = Finder::create()->files()->name('sitemap*.xml')->in($permanentDir);
176
        foreach ($oldFiles as $file) {
177
            $this->filesystem->remove($file->getRealPath());
178
        }
179
180
        $newFiles = Finder::create()->files()->name('sitemap*.xml')->in($tempDir);
181
        foreach ($newFiles as $file) {
182
            $this->filesystem->rename($file->getRealPath(), sprintf('%s/%s', $permanentDir, $file->getFilename()));
183
        }
184
    }
185
}
186