Completed
Pull Request — 2.x (#345)
by
unknown
01:11
created

SitemapGeneratorCommand::execute()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 9.44
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 Sonata\Exporter\Handler;
17
use Sonata\Exporter\Writer\SitemapWriter;
18
use Sonata\SeoBundle\Sitemap\SourceManager;
19
use Symfony\Component\Console\Command\Command;
20
use Symfony\Component\Console\Input\InputArgument;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Input\InputOption;
23
use Symfony\Component\Console\Output\OutputInterface;
24
use Symfony\Component\Filesystem\Filesystem;
25
use Symfony\Component\Finder\Finder;
26
use Symfony\Component\Routing\RequestContext;
27
use Symfony\Component\Routing\RouterInterface;
28
29
/**
30
 * Create a sitemap.
31
 *
32
 * @author Thomas Rabaix <[email protected]>
33
 */
34
class SitemapGeneratorCommand extends Command
35
{
36
    /**
37
     * @var RouterInterface
38
     */
39
    private $router;
40
41
    /**
42
     * @var SourceManager
43
     */
44
    private $sitemapManager;
45
46
    /**
47
     * @var Filesystem
48
     */
49
    private $fs;
50
51
    public function __construct(RouterInterface $router, SourceManager $sitemapManager, Filesystem $fs)
52
    {
53
        $this->router = $router;
54
        $this->sitemapManager = $sitemapManager;
55
        $this->fs = $fs;
56
57
        parent::__construct();
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function configure()
64
    {
65
        $this->setName('sonata:seo:sitemap');
66
67
        $this->addArgument('folder', InputArgument::REQUIRED, 'The folder to store the sitemap.xml file');
68
        $this->addArgument('host', InputArgument::REQUIRED, 'Set the host');
69
        $this->addOption('scheme', null, InputOption::VALUE_OPTIONAL, 'Set the scheme', 'http');
70
        $this->addOption('baseurl', null, InputOption::VALUE_OPTIONAL, 'Set the base url', '');
71
        $this->addOption('sitemap_path', null, InputOption::VALUE_OPTIONAL, 'Set the sitemap relative path (if in a specific folder)', '');
72
73
        $this->setDescription('Create a sitemap');
74
        $this->setHelp(<<<'EOT'
75
The <info>sonata:seo:sitemap</info> command create new sitemap files (index + sitemap).
76
77
EOT
78
        );
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84
    public function execute(InputInterface $input, OutputInterface $output)
85
    {
86
        $host = $input->getArgument('host');
87
        $scheme = $input->getOption('scheme');
88
        $baseUrl = $input->getOption('baseurl');
89
        $permanentFolder = $input->getArgument('folder');
90
        $appendPath = $input->hasOption('sitemap_path') ? $input->getOption('sitemap_path') : $baseUrl;
91
92
        $this->getContext()->setHost($host);
0 ignored issues
show
Bug introduced by
It seems like $host defined by $input->getArgument('host') on line 86 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...
93
        $this->getContext()->setScheme($scheme);
94
        $this->getContext()->setBaseUrl($baseUrl);
95
96
        $tempFolder = $this->createTempFolder($output);
97
        if ($tempFolder === null) {
98
            $output->writeln('<error>The temporary folder already exists</error>');
99
            $output->writeln('<error>If the task is not running please delete this folder</error>');
100
            return 1;
101
        }
102
103
        $output->writeln(sprintf('Generating sitemap - this can take a while'));
104
        $this->generateSitemap($tempFolder, $scheme, $host, $appendPath);
0 ignored issues
show
Bug introduced by
It seems like $host defined by $input->getArgument('host') on line 86 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...
105
106
        $output->writeln(sprintf('Moving temporary file to %s ...', $permanentFolder));
107
        $this->moveTemporaryFile($tempFolder, $permanentFolder);
0 ignored issues
show
Bug introduced by
It seems like $permanentFolder defined by $input->getArgument('folder') on line 89 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...
108
109
        $output->writeln('Cleanup ...');
110
        $this->fs->remove($tempFolder);
111
112
        $output->writeln('<info>done!</info>');
113
    }
114
115
    private function getContext(): RequestContext
116
    {
117
        return $this->router->getContext();
118
    }
119
120
    /**
121
     * Creates temporary folder if one does not exist.
122
     * @return string|null Folder name or null if folder is already exist.
123
     */
124
    private function createTempFolder(OutputInterface $output)
125
    {
126
        $tempFolder = sys_get_temp_dir().'/sonata_sitemap_'.md5(__DIR__);
127
128
        $output->writeln(sprintf('Creating temporary folder: %s', $tempFolder));
129
130
        if ($this->fs->exists($tempFolder)) {
131
            return null;
132
        }
133
134
        $this->fs->mkdir($tempFolder);
135
136
        return $tempFolder;
137
    }
138
139
    /**
140
     * @throws \Exception
141
     */
142
    private function generateSitemap(string $folder, string $scheme, string $host, string $appendPath): void
143
    {
144
        foreach ($this->sitemapManager as $group => $sitemap) {
145
            $write = new SitemapWriter($folder, $group, $sitemap->types, false);
146
147
            try {
148
                Handler::create($sitemap->sources, $write)->export();
149
            } catch (\Exception $e) {
150
                $this->fs->remove($folder);
151
152
                throw $e;
153
            }
154
        }
155
156
        // generate global sitemap index
157
        SitemapWriter::generateSitemapIndex(
158
            $folder,
159
            sprintf('%s://%s%s', $scheme, $host, $appendPath),
160
            'sitemap*.xml',
161
            'sitemap.xml'
162
        );
163
    }
164
165
    private function moveTemporaryFile(string $tempFolder, string $permanentFolder): void
166
    {
167
        $oldFiles = Finder::create()->files()->name('sitemap*.xml')->in($permanentFolder);
168
        foreach ($oldFiles as $file) {
169
            $this->fs->remove($file->getRealPath());
170
        }
171
172
        $newFiles = Finder::create()->files()->name('sitemap*.xml')->in($tempFolder);
173
        foreach ($newFiles as $file) {
174
            $this->fs->rename($file->getRealPath(), sprintf('%s/%s', $permanentFolder, $file->getFilename()));
175
        }
176
    }
177
}
178