Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Для профи > XmlWriter вывод в php://output


Автор: Logree 14.2.2016, 16:49
Здравствуйте, помогите разобраться с проблемой: есть простая задача - сгенерировать sitemap.xml.
Хочу генирировать код сразу в php://output, делаю так:
Код

class SitemapXmlGenerator extends \XMLWriter
{
    /**
     * SitemapXmlGenerator constructor.
     */
    public function __construct()
    {
        $response = Yii::$app->response;
        $response->format = Response::FORMAT_RAW;
        $response->getHeaders()->set('Content-Type', 'text/xml; charset=utf-8');
    }

    /**
     * @inheritdoc
     */
    public function execute($dstResource) {

        //ob_start();

        if ($this->openUri($dstResource) === false) {

            throw new Exception("Can't open resource {$dstResource}");
        };

        $this->startDocument('1.0', 'UTF-8');
        $this->setIndent(true);
        $this->setIndentString("\t");

        $this->startElement('urlset');
            $this->writeAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');

        // urls

        $this->writeUrls();

        $this->endElement(); // urlset
        $this->endDocument();

        //ob_end_flush();
    }

    public function writeUrls()
    {
        $urls = $this->getUrls();

        foreach ($rls->batch(100) as $part) {
            foreach ($part as $url) {
                $this->writeUrl($url);
            }
        }
    }

public function writeUrl($loc, $priority = 0.5, $changeFreq = 'weekly', $lastMod = null)
    {
        $flags = \ENT_QUOTES | \ENT_XML1;

        if (empty($loc) || strlen($loc) > self::LOC_MAX_LEN || strpos($loc, 'http') !== 0) {
            return;
        }

        $this->startElement('url');
            $this->writeElement('loc', \htmlspecialchars($loc, $flags));
        if ($priority !== null) {
            $this->writeElement('priority', $priority);
        }
        if ($changeFreq !== null) {
            $this->writeElement('changefreq', $changeFreq);
        }
        if ($lastMod !== null) {
            $this->writeElement('lastmod', $lastMod);
        }
        $this->endElement(); // url
    }

}

просто генерируем документ наследуясь от XMLWriter
Код

$g = new SitemapXmlGenerator();
$g->execute('php://output');

Все работает, все красиво, но для больших объемов данных ответ приходил с Content-type: text/html вместо text/xml хотя все генерировалось полностью. 
Оказалось что заголовки отправляются как только размер переваливает за 8016 байт. т.е скидывается буфер из-за этого меняется Content-type, если я все правильно понимаю.

Пробывал спользовать ob_start(), как в примере выше (закоментированно) - без результата.

Подскажите как правильно сделать? Можно ли реализовать без использования временного файла?

Автор: Logree 14.2.2016, 18:38
Странно что ни кто не дал ответ.

Судя по всему Yii2 по какой-то причине в данном случае не проставляет (не успевает) изменить заголовки  в конструкторе:
Код

/**
     * SitemapXmlGenerator constructor.
     */
    public function __construct()
    {
        $response = Yii::$app->response;
        $response->format = Response::FORMAT_RAW;
        $response->getHeaders()->set('Content-Type', 'text/xml; charset=utf-8');
    }

заменяем на:
Код

/**
     * SitemapXmlGenerator constructor.
     */
    public function __construct()
    {
        header('Content-Type: text/xml; charset=utf-8');
    }

И все работает. 
Если кто-то, на пример SamDark, сможет дать комментарий, буду благодарен)

Автор: Logree 15.2.2016, 12:24
Поторопился - проблема НЕ решена.  Поделитесь мыслями.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)