Completed
Pull Request — master (#358)
by Christian
01:29
created

SitemapGeneratorCommand::getContainer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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