This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

21
vendor/sonata-project/exporter/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2010 Thomas Rabaix
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

60
vendor/sonata-project/exporter/Makefile vendored Normal file
View File

@@ -0,0 +1,60 @@
# DO NOT EDIT THIS FILE!
#
# It's auto-generated by sonata-project/dev-kit package.
all:
@echo "Please choose a task."
.PHONY: all
lint: lint-composer lint-yaml lint-composer lint-xml lint-php
.PHONY: lint
lint-composer:
composer validate
.PHONY: lint-composer
lint-yaml:
yaml-lint --ignore-non-yaml-files --quiet --exclude vendor .
.PHONY: lint-yaml
lint-xml:
find . \( -name '*.xml' -or -name '*.xliff' \) \
-not -path './vendor/*' \
-not -path './src/Resources/public/vendor/*' \
| while read xmlFile; \
do \
XMLLINT_INDENT=' ' xmllint --encode UTF-8 --format "$$xmlFile"|diff - "$$xmlFile"; \
if [ $$? -ne 0 ] ;then exit 1; fi; \
done
.PHONY: lint-xml
lint-php:
php-cs-fixer fix --ansi --verbose --diff --dry-run
.PHONY: lint-php
cs-fix: cs-fix-php cs-fix-xml
.PHONY: cs-fix
cs-fix-php:
php-cs-fixer fix --verbose
.PHONY: cs-fix-php
cs-fix-xml:
find . \( -name '*.xml' -or -name '*.xliff' \) \
-not -path './vendor/*' \
-not -path './src/Resources/public/vendor/*' \
| while read xmlFile; \
do \
XMLLINT_INDENT=' ' xmllint --encode UTF-8 --format "$$xmlFile" --output "$$xmlFile"; \
done
.PHONY: cs-fix-xml
test:
phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml
.PHONY: test
docs:
cd docs && sphinx-build -W -b html -d _build/doctrees . _build/html
.PHONY: docs

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Bridge\Symfony\Bundle;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\SonataExporterBundle', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\SonataExporterBundle class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\SonataExporterBundle instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\SonataExporterBundle',
__NAMESPACE__.'\SonataExporterBundle'
);
if (false) {
final class SonataExporterBundle extends \Sonata\Exporter\Bridge\Symfony\Bundle\SonataExporterBundle
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Bridge\Symfony\DependencyInjection\Compiler;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\ExporterCompilerPass', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\ExporterCompilerPass class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\ExporterCompilerPass instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\ExporterCompilerPass',
__NAMESPACE__.'\ExporterCompilerPass'
);
if (false) {
/**
* @deprecated since version 1.x, to be removed in 2.0.
*/
final class ExporterCompilerPass extends \Sonata\Exporter\Bridge\Symfony\DependencyInjection\Compiler\ExporterCompilerPass
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Bridge\Symfony\DependencyInjection;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\Configuration', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\Configuration class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\Configuration instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\Configuration',
__NAMESPACE__.'\Configuration'
);
if (false) {
final class Configuration extends \Sonata\Exporter\Bridge\Symfony\DependencyInjection\Configuration
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Bridge\Symfony\DependencyInjection;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\SonataExporterExtension', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\SonataExporterExtension class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\SonataExporterExtension instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\SonataExporterExtension',
__NAMESPACE__.'\SonataExporterExtension'
);
if (false) {
/**
* @deprecated since version 1.x, to be removed in 2.0.
*/
final class SonataExporterExtension extends \Sonata\Exporter\Bridge\Symfony\DependencyInjection\SonataExporterExtension
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Exception;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\InvalidDataFormatException', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\InvalidDataFormatException class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\InvalidDataFormatException instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\InvalidDataFormatException',
__NAMESPACE__.'\InvalidDataFormatException'
);
if (false) {
class InvalidDataFormatException extends \Sonata\Exporter\Exception\InvalidDataFormatException
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Exception;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\InvalidMethodCallException', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\InvalidMethodCallException class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\InvalidMethodCallException instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\InvalidMethodCallException',
__NAMESPACE__.'\InvalidMethodCallException'
);
if (false) {
class InvalidMethodCallException extends \Sonata\Exporter\Exception\InvalidMethodCallException
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\Exporter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\Exporter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\Exporter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\Exporter',
__NAMESPACE__.'\Exporter'
);
if (false) {
/**
* @deprecated since version 1.x, to be removed in 2.0.
*/
final class Exporter
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\Handler', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\Handler class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\Handler instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\Handler',
__NAMESPACE__.'\Handler'
);
if (false) {
class Handler extends \Sonata\Exporter\Handler
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\AbstractXmlSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\AbstractXmlSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\AbstractXmlSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\AbstractXmlSourceIterator',
__NAMESPACE__.'\AbstractXmlSourceIterator'
);
if (false) {
abstract class AbstractXmlSourceIterator extends \Sonata\Exporter\Source\AbstractXmlSourceIterator
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\ArraySourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\ArraySourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\ArraySourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\ArraySourceIterator',
__NAMESPACE__.'\ArraySourceIterator'
);
if (false) {
/**
* @deprecated since version 1.x, to be removed in 2.0.
*/
class ArraySourceIterator extends \Sonata\Exporter\Source\ArraySourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\ChainSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\ChainSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\ChainSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\ChainSourceIterator',
__NAMESPACE__.'\ChainSourceIterator'
);
if (false) {
class ChainSourceIterator extends \Sonata\Exporter\Source\ChainSourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\CsvSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\CsvSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\CsvSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\CsvSourceIterator',
__NAMESPACE__.'\CsvSourceIterator'
);
if (false) {
class CsvSourceIterator extends \Sonata\Exporter\Source\CsvSourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\DoctrineDBALConnectionSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\DoctrineDBALConnectionSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\DoctrineDBALConnectionSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\DoctrineDBALConnectionSourceIterator',
__NAMESPACE__.'\DoctrineDBALConnectionSourceIterator'
);
if (false) {
class DoctrineDBALConnectionSourceIterator extends \Sonata\Exporter\Source\DoctrineDBALConnectionSourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\DoctrineODMQuerySourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\DoctrineODMQuerySourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\DoctrineODMQuerySourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\DoctrineODMQuerySourceIterator',
__NAMESPACE__.'\DoctrineODMQuerySourceIterator'
);
if (false) {
class DoctrineODMQuerySourceIterator extends \Sonata\Exporter\Source\DoctrineODMQuerySourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\DoctrineORMQuerySourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\DoctrineORMQuerySourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\DoctrineORMQuerySourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\DoctrineORMQuerySourceIterator',
__NAMESPACE__.'\DoctrineORMQuerySourceIterator'
);
if (false) {
class DoctrineORMQuerySourceIterator extends \Sonata\Exporter\Source\DoctrineORMQuerySourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\IteratorCallbackSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\IteratorCallbackSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\IteratorCallbackSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\IteratorCallbackSourceIterator',
__NAMESPACE__.'\IteratorCallbackSourceIterator'
);
if (false) {
class IteratorCallbackSourceIterator extends \Sonata\Exporter\Source\IteratorCallbackSourceIterator
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\IteratorSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\IteratorSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\IteratorSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\IteratorSourceIterator',
__NAMESPACE__.'\IteratorSourceIterator'
);
if (false) {
/**
* @deprecated since version 1.x, to be removed in 2.0.
*/
class IteratorSourceIterator extends \Sonata\Exporter\Source\IteratorSourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\PDOStatementSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\PDOStatementSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\PDOStatementSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\PDOStatementSourceIterator',
__NAMESPACE__.'\PDOStatementSourceIterator'
);
if (false) {
class PDOStatementSourceIterator extends \Sonata\Exporter\Source\PDOStatementSourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\PropelCollectionSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\PropelCollectionSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\PropelCollectionSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\PropelCollectionSourceIterator',
__NAMESPACE__.'\PropelCollectionSourceIterator'
);
if (false) {
class PropelCollectionSourceIterator extends \Sonata\Exporter\Source\PropelCollectionSourceIterator
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!interface_exists('\Sonata\\'.__NAMESPACE__.'\SourceIteratorInterface', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\SourceIteratorInterface class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\SourceIteratorInterface instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\SourceIteratorInterface',
__NAMESPACE__.'\SourceIteratorInterface'
);
if (false) {
/**
* @deprecated since version 1.x, to be removed in 2.0.
*/
interface SourceIteratorInterface extends \Sonata\Exporter\Source\SourceIteratorInterface
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\SymfonySitemapSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\SymfonySitemapSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\SymfonySitemapSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\SymfonySitemapSourceIterator',
__NAMESPACE__.'\SymfonySitemapSourceIterator'
);
if (false) {
class SymfonySitemapSourceIterator extends \Sonata\Exporter\Source\SymfonySitemapSourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\XmlExcelSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\XmlExcelSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\XmlExcelSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\XmlExcelSourceIterator',
__NAMESPACE__.'\XmlExcelSourceIterator'
);
if (false) {
class XmlExcelSourceIterator extends \Sonata\Exporter\Source\XmlExcelSourceIterator
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Source;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\XmlSourceIterator', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\XmlSourceIterator class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\XmlSourceIterator instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\XmlSourceIterator',
__NAMESPACE__.'\XmlSourceIterator'
);
if (false) {
class XmlSourceIterator extends \Sonata\Exporter\Source\XmlSourceIterator
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Test;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\AbstractTypedWriterTestCase', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\AbstractTypedWriterTestCase class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\AbstractTypedWriterTestCase instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\AbstractTypedWriterTestCase',
__NAMESPACE__.'\AbstractTypedWriterTestCase'
);
if (false) {
/**
* @deprecated since version 1.x, to be removed in 2.0.
*/
abstract class AbstractTypedWriterTestCase extends \Sonata\Exporter\Test\AbstractTypedWriterTestCase
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\CsvWriter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\CsvWriter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\CsvWriter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\CsvWriter',
__NAMESPACE__.'\CsvWriter'
);
if (false) {
class CsvWriter extends \Sonata\Exporter\Writer\CsvWriter
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\CsvWriterTerminate', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\CsvWriterTerminate class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\CsvWriterTerminate instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\CsvWriterTerminate',
__NAMESPACE__.'\CsvWriterTerminate'
);
if (false) {
final class CsvWriterTerminate extends \Sonata\Exporter\Writer\CsvWriterTerminate
{
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\FormattedBoolWriter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\FormattedBoolWriter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\FormattedBoolWriter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\FormattedBoolWriter',
__NAMESPACE__.'\FormattedBoolWriter'
);
if (false) {
class FormattedBoolWriter extends \Sonata\Exporter\Writer\FormattedBoolWriter
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\GsaFeedWriter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\GsaFeedWriter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\GsaFeedWriter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\GsaFeedWriter',
__NAMESPACE__.'\GsaFeedWriter'
);
if (false) {
class GsaFeedWriter extends \Sonata\Exporter\Writer\GsaFeedWriter
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\InMemoryWriter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\InMemoryWriter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\InMemoryWriter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\InMemoryWriter',
__NAMESPACE__.'\InMemoryWriter'
);
if (false) {
class InMemoryWriter extends \Sonata\Exporter\Writer\InMemoryWriter
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\JsonWriter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\JsonWriter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\JsonWriter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\JsonWriter',
__NAMESPACE__.'\JsonWriter'
);
if (false) {
class JsonWriter extends \Sonata\Exporter\Writer\JsonWriter
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\SitemapWriter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\SitemapWriter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\SitemapWriter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\SitemapWriter',
__NAMESPACE__.'\SitemapWriter'
);
if (false) {
class SitemapWriter extends \Sonata\Exporter\Writer\SitemapWriter
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!interface_exists('\Sonata\\'.__NAMESPACE__.'\TypedWriterInterface', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\TypedWriterInterface class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\TypedWriterInterface instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\TypedWriterInterface',
__NAMESPACE__.'\TypedWriterInterface'
);
if (false) {
/**
* @deprecated since version 1.x, to be removed in 2.0.
*/
interface TypedWriterInterface extends \Sonata\Exporter\Writer\TypedWriterInterface
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!interface_exists('\Sonata\\'.__NAMESPACE__.'\WriterInterface', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\WriterInterface class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\WriterInterface instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\WriterInterface',
__NAMESPACE__.'\WriterInterface'
);
if (false) {
/**
* @deprecated since version 1.x, to be removed in 2.0.
*/
interface WriterInterface extends Exporter\Writer
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\XlsWriter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\XlsWriter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\XlsWriter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\XlsWriter',
__NAMESPACE__.'\XlsWriter'
);
if (false) {
class XlsWriter extends \Sonata\Exporter\Writer\XlsWriter
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\XmlExcelWriter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\XmlExcelWriter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\XmlExcelWriter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\XmlExcelWriter',
__NAMESPACE__.'\XmlExcelWriter'
);
if (false) {
class XmlExcelWriter extends \Sonata\Exporter\Writer\XmlExcelWriter
{
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Exporter\Writer;
if (!class_exists('\Sonata\\'.__NAMESPACE__.'\XmlWriter', false)) {
@trigger_error(
'The '.__NAMESPACE__.'\XmlWriter class is deprecated since version 1.x and will be removed in 2.0.'
.' Use \Sonata\\'.__NAMESPACE__.'\XmlWriter instead',
E_USER_DEPRECATED
);
}
class_alias(
'\Sonata\\'.__NAMESPACE__.'\XmlWriter',
__NAMESPACE__.'\XmlWriter'
);
if (false) {
class XmlWriter extends \Sonata\Exporter\Writer\XmlWriter
{
}
}

View File

@@ -0,0 +1,63 @@
{
"name": "sonata-project/exporter",
"type": "library",
"description": "Lightweight Exporter library",
"keywords": [
"export",
"csv",
"xls",
"data",
"client"
],
"homepage": "https://github.com/sonata-project/Exporter",
"license": "MIT",
"authors": [
{
"name": "Thomas Rabaix",
"email": "thomas.rabaix@gmail.com",
"homepage": "https://sonata-project.org/"
}
],
"require": {
"php": "^5.6 || ^7.0"
},
"require-dev": {
"doctrine/dbal": "^2.5",
"doctrine/orm": "^2.4.5",
"matthiasnoback/symfony-config-test": "^2.0",
"matthiasnoback/symfony-dependency-injection-test": "^1.0",
"propel/propel1": "^1.6",
"symfony/config": "^2.8 || ^3.2 || ^4.0",
"symfony/dependency-injection": "^2.8 || ^3.2 || ^4.0",
"symfony/http-foundation": "^2.8 || ^3.2 || ^4.0",
"symfony/http-kernel": "^2.8 || ^3.2 || ^4.0",
"symfony/phpunit-bridge": "^4.0",
"symfony/property-access": "^2.8 || ^3.2 || ^4.0",
"symfony/routing": "^2.8 || ^3.2 || ^4.0"
},
"suggest": {
"ext-curl": "*",
"propel/propel1": "^1.6",
"symfony/property-access": "To be able to export from database entities",
"symfony/routing": "To be able to export the routes of a Symfony app"
},
"config": {
"sort-packages": true
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Exporter\\": "aliases/",
"Sonata\\Exporter\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Exporter\\Test\\": "tests/"
}
}
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
DO NOT EDIT THIS FILE!
It's auto-generated by sonata-project/dev-kit package.
-->
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
bootstrap="tests/bootstrap.php"
>
<testsuites>
<testsuite name="Sonata Exporter Test Suite">
<directory suffix="Test.php">./tests/</directory>
</testsuite>
</testsuites>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
</listeners>
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
</whitelist>
</filter>
<php>
<ini name="precision" value="8"/>
</php>
</phpunit>

View File

@@ -0,0 +1,37 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Bridge\Symfony\Bundle;
use Sonata\Exporter\Bridge\Symfony\DependencyInjection\Compiler\ExporterCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
final class SonataExporterBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new ExporterCompilerPass());
}
/**
* {@inheritdoc}
*/
protected function getContainerExtensionClass()
{
return 'Exporter\Bridge\Symfony\DependencyInjection\SonataExporterExtension';
}
}
class_exists(\Exporter\Bridge\Symfony\Bundle\SonataExporterBundle::class);

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Bridge\Symfony\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Grégoire Paris <postmaster@greg0ire.fr>
*/
final class ExporterCompilerPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has('sonata.exporter.exporter')) {
return;
}
$definition = $container->findDefinition('sonata.exporter.exporter');
$writers = $container->findTaggedServiceIds('sonata.exporter.writer');
foreach (array_keys($writers) as $id) {
$definition->addMethodCall('addWriter', [new Reference($id)]);
}
}
}
class_exists(\Exporter\Bridge\Symfony\DependencyInjection\Compiler\ExporterCompilerPass::class);

View File

@@ -0,0 +1,127 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Bridge\Symfony\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files.
*
* @author Grégoire Paris <postmaster@greg0ire.fr>
*/
final class Configuration implements ConfigurationInterface
{
/**
* {@inheritdoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('sonata_exporter');
$rootNode
->children()
->arrayNode('exporter')
->addDefaultsIfNotSet()
->children()
->arrayNode('default_writers')
->defaultValue(['csv', 'json', 'xls', 'xml'])
->prototype('scalar')->end()
->end()
->end()
->end()
->arrayNode('writers')
->addDefaultsIfNotSet()
->children()
->arrayNode('csv')
->addDefaultsIfNotSet()
->children()
->scalarNode('filename')
->defaultValue('php://output')
->info('path to the output file')
->end()
->scalarNode('delimiter')
->defaultValue(',')
->info('delimits csv values')
->end()
->scalarNode('enclosure')
->defaultValue('"')
->info('will be used when a value contains the delimiter')
->end()
->scalarNode('escape')
->defaultValue('\\')
->info('will be used when a value contains the enclosure')
->end()
->booleanNode('show_headers')
->defaultValue(true)
->info('add column names as the first line')
->end()
->booleanNode('with_bom')
->defaultValue(false)
->info('include the byte order mark')
->end()
->end()
->end()
->arrayNode('json')
->addDefaultsIfNotSet()
->children()
->scalarNode('filename')
->defaultValue('php://output')
->info('path to the output file')
->end()
->end()
->end()
->arrayNode('xls')
->addDefaultsIfNotSet()
->children()
->scalarNode('filename')
->defaultValue('php://output')
->info('path to the output file')
->end()
->booleanNode('show_headers')
->defaultValue(true)
->info('add column names as the first line')
->end()
->end()
->end()
->arrayNode('xml')
->addDefaultsIfNotSet()
->children()
->scalarNode('filename')
->defaultValue('php://output')
->info('path to the output file')
->end()
->booleanNode('show_headers')
->defaultValue(true)
->info('add column names as the first line')
->end()
->scalarNode('main_element')
->defaultValue('datas')
->info('name of the wrapping element')
->end()
->scalarNode('child_element')
->defaultValue('data')
->info('name of elements corresponding to rows')
->end()
->end()
->end()
->end()
->end()
->end()
;
return $treeBuilder;
}
}
class_exists(\Exporter\Bridge\Symfony\DependencyInjection\Configuration::class);

View File

@@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Bridge\Symfony\DependencyInjection;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/**
* @author Grégoire Paris <postmaster@greg0ire.fr>
*/
final class SonataExporterExtension extends Extension
{
/**
* {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$processor = new Processor();
$configuration = new Configuration();
$config = $processor->processConfiguration($configuration, $configs);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
$this->configureExporter($container, $config['exporter']);
$this->configureWriters($container, $config['writers']);
}
private function configureExporter(ContainerBuilder $container, array $config)
{
foreach (['csv', 'json', 'xls', 'xml'] as $format) {
if (\in_array($format, $config['default_writers'])) {
$container->getDefinition('sonata.exporter.writer.'.$format)->addTag(
'sonata.exporter.writer'
);
}
}
}
private function configureWriters(ContainerBuilder $container, array $config)
{
foreach ($config as $format => $settings) {
foreach ($settings as $key => $value) {
$container->setParameter(sprintf(
'sonata.exporter.writer.%s.%s',
$format,
$key
), $value);
}
}
}
}
class_exists(\Exporter\Bridge\Symfony\DependencyInjection\SonataExporterExtension::class);

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.exporter.writer.csv" class="Exporter\Writer\CsvWriter" public="false">
<argument>%sonata.exporter.writer.csv.filename%</argument>
<argument>%sonata.exporter.writer.csv.delimiter%</argument>
<argument>%sonata.exporter.writer.csv.enclosure%</argument>
<argument>%sonata.exporter.writer.csv.escape%</argument>
<argument>%sonata.exporter.writer.csv.show_headers%</argument>
<argument>%sonata.exporter.writer.csv.with_bom%</argument>
</service>
<service id="sonata.exporter.writer.json" class="Exporter\Writer\JsonWriter" public="false">
<argument>%sonata.exporter.writer.json.filename%</argument>
</service>
<service id="sonata.exporter.writer.xls" class="Exporter\Writer\XlsWriter" public="false">
<argument>%sonata.exporter.writer.xls.filename%</argument>
<argument>%sonata.exporter.writer.xls.show_headers%</argument>
</service>
<service id="sonata.exporter.writer.xml" class="Exporter\Writer\XmlWriter" public="false">
<argument>%sonata.exporter.writer.xml.filename%</argument>
<argument>%sonata.exporter.writer.xml.main_element%</argument>
<argument>%sonata.exporter.writer.xml.child_element%</argument>
</service>
<service id="sonata.exporter.exporter" class="Exporter\Exporter" public="true"/>
<service id="Exporter\Exporter" alias="sonata.exporter.exporter"/>
</services>
</container>

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Exception;
class InvalidDataFormatException extends \RuntimeException
{
}
class_exists(\Exporter\Exception\InvalidDataFormatException::class);

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Exception;
class InvalidMethodCallException extends \RuntimeException
{
}
class_exists(\Exporter\Exception\InvalidMethodCallException::class);

View File

@@ -0,0 +1,95 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter;
use Sonata\Exporter\Source\SourceIteratorInterface;
use Sonata\Exporter\Writer\TypedWriterInterface;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* @author Grégoire Paris <postmaster@greg0ire.fr>
*/
final class Exporter
{
/**
* @var TypedWriterInterface[]
*/
private $writers;
/**
* @param TypedWriterInterface[] $writers an array of allowed typed writers, indexed by format name
*/
public function __construct(array $writers = [])
{
$this->writers = [];
foreach ($writers as $writer) {
$this->addWriter($writer);
}
}
/**
* @param string $format
* @param string $filename
* @param SourceIteratorInterface $source
*
* @throws \RuntimeException
*
* @return StreamedResponse
*/
public function getResponse($format, $filename, SourceIteratorInterface $source)
{
if (!\array_key_exists($format, $this->writers)) {
throw new \RuntimeException(sprintf(
'Invalid "%s" format, supported formats are : "%s"',
$format,
implode(', ', array_keys($this->writers))
));
}
$writer = $this->writers[$format];
$callback = function () use ($source, $writer) {
$handler = \Exporter\Handler::create($source, $writer);
$handler->export();
};
$headers = [
'Content-Disposition' => sprintf('attachment; filename="%s"', $filename),
];
$headers['Content-Type'] = $writer->getDefaultMimeType();
return new StreamedResponse($callback, 200, $headers);
}
/**
* Returns a simple array of export formats.
*
* @return string[] writer formats as returned by the TypedWriterInterface::getFormat() method
*/
public function getAvailableFormats()
{
return array_keys($this->writers);
}
/**
* The main benefit of this method is the type hinting.
*
* @param TypedWriterInterface $writer a possible writer for exporting data
*/
public function addWriter(TypedWriterInterface $writer)
{
$this->writers[$writer->getFormat()] = $writer;
}
}
class_exists(\Exporter\Exporter::class);

View File

@@ -0,0 +1,62 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter;
use Sonata\Exporter\Source\SourceIteratorInterface;
use Sonata\Exporter\Writer\WriterInterface;
class Handler
{
/**
* @var SourceIteratorInterface
*/
protected $source;
/**
* @var WriterInterface
*/
protected $writer;
/**
* @param SourceIteratorInterface $source
* @param WriterInterface $writer
*/
public function __construct(SourceIteratorInterface $source, WriterInterface $writer)
{
$this->source = $source;
$this->writer = $writer;
}
public function export()
{
$this->writer->open();
foreach ($this->source as $data) {
$this->writer->write($data);
}
$this->writer->close();
}
/**
* @param SourceIteratorInterface $source
* @param WriterInterface $writer
*
* @return Handler
*/
public static function create(SourceIteratorInterface $source, WriterInterface $writer)
{
return new self($source, $writer);
}
}
class_exists(\Exporter\Handler::class);

View File

@@ -0,0 +1,221 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
/**
* Read data from a Xml file.
*
* @author Vincent Touzet <vincent.touzet@gmail.com>
*/
abstract class AbstractXmlSourceIterator implements SourceIteratorInterface
{
/**
* @var string
*/
protected $filename = null;
/**
* @var resource
*/
protected $file = null;
/**
* @var bool|null
*/
protected $hasHeaders = null;
/**
* @var string[]
*/
protected $columns = [];
/**
* @var resource
*/
protected $parser = null;
/**
* @var int
*/
protected $currentRowIndex = 0;
/**
* @var int
*/
protected $currentColumnIndex = 0;
/**
* @var mixed
*/
protected $currentRow = null;
/**
* @var array
*/
protected $bufferedRow = [];
/**
* @var bool
*/
protected $currentRowEnded = false;
/**
* @var int
*/
protected $position = 0;
/**
* @param string $filename
* @param bool $hasHeaders
*/
public function __construct($filename, $hasHeaders = true)
{
$this->filename = $filename;
$this->hasHeaders = $hasHeaders;
}
/**
* Start element handler.
*
* @param resource $parser
* @param string $name
* @param array $attributes
*/
abstract public function tagStart($parser, $name, $attributes = []);
/**
* End element handler.
*
* @param resource $parser
* @param string $name
*/
abstract public function tagEnd($parser, $name);
/**
* Tag content handler.
*
* @param resource $parser
* @param string $data
*/
abstract public function tagContent($parser, $data);
/**
* {@inheritdoc}
*/
public function current()
{
return $this->currentRow;
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->position;
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->parseRow();
$this->prepareCurrentRow();
++$this->position;
}
/**
* {@inheritdoc}
*/
public function rewind()
{
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_set_element_handler($this->parser, 'tagStart', 'tagEnd');
xml_set_character_data_handler($this->parser, 'tagContent');
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 0);
$this->file = fopen($this->filename, 'r');
$this->bufferedRow = [];
$this->currentRowIndex = 0;
$this->currentColumnIndex = 0;
$this->position = 0;
$this->parseRow();
if ($this->hasHeaders) {
$this->columns = array_shift($this->bufferedRow);
$this->parseRow();
}
$this->prepareCurrentRow();
}
/**
* {@inheritdoc}
*/
public function valid()
{
if (!\is_array($this->currentRow)) {
xml_parser_free($this->parser);
fclose($this->file);
return false;
}
return true;
}
/**
* Parse until </Row> reached.
*/
protected function parseRow()
{
// only parse the next row if only one in buffer
if (\count($this->bufferedRow) > 1) {
return;
}
if (feof($this->file)) {
$this->currentRow = null;
return;
}
$this->currentRowEnded = false;
// read file until row is ended
while (!$this->currentRowEnded && !feof($this->file)) {
$data = fread($this->file, 1024);
xml_parse($this->parser, $data);
}
}
/**
* Prepare the row to return.
*/
protected function prepareCurrentRow()
{
$this->currentRow = array_shift($this->bufferedRow);
if (\is_array($this->currentRow)) {
$datas = [];
foreach ($this->currentRow as $key => $value) {
if ($this->hasHeaders) {
$datas[$this->columns[$key]] = html_entity_decode($value);
} else {
$datas[$key] = html_entity_decode($value);
}
}
$this->currentRow = $datas;
}
}
}
class_exists(\Exporter\Source\AbstractXmlSourceIterator::class);

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
class ArraySourceIterator extends IteratorSourceIterator
{
/**
* @param array $data
*/
public function __construct(array $data)
{
parent::__construct(new \ArrayIterator($data));
}
}
class_exists(\Exporter\Source\ArraySourceIterator::class);

View File

@@ -0,0 +1,96 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
use ArrayIterator;
class ChainSourceIterator implements SourceIteratorInterface
{
/**
* @var ArrayIterator
*/
protected $sources;
/**
* @param array $sources
*/
public function __construct(array $sources = [])
{
$this->sources = new ArrayIterator();
foreach ($sources as $source) {
$this->addSource($source);
}
}
/**
* @param SourceIteratorInterface $source
*/
public function addSource(SourceIteratorInterface $source)
{
$this->sources->append($source);
}
/**
* {@inheritdoc}
*/
public function current()
{
return $this->sources->current()->current();
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->sources->current()->next();
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->sources->current()->key();
}
/**
* {@inheritdoc}
*/
public function valid()
{
while (!$this->sources->current()->valid()) {
$this->sources->next();
if (!$this->sources->valid()) {
return false;
}
$this->sources->current()->rewind();
}
return true;
}
/**
* {@inheritdoc}
*/
public function rewind()
{
if ($this->sources->current()) {
$this->sources->current()->rewind();
}
}
}
class_exists(\Exporter\Source\ChainSourceIterator::class);

View File

@@ -0,0 +1,159 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
/**
* Read data from a csv file.
*
* @author Vincent Touzet <vincent.touzet@gmail.com>
*/
class CsvSourceIterator implements SourceIteratorInterface
{
/**
* @var string
*/
protected $filename = null;
/**
* @var resource
*/
protected $file = null;
/**
* @var string|null
*/
protected $delimiter = null;
/**
* @var string|null
*/
protected $enclosure = null;
/**
* @var string|null
*/
protected $escape = null;
/**
* @var bool|null
*/
protected $hasHeaders = null;
/**
* @var array
*/
protected $lines = [];
/**
* @var array
*/
protected $columns = [];
/**
* @var int
*/
protected $position = 0;
/**
* @var array
*/
protected $currentLine = [];
/**
* @param string $filename
* @param string $delimiter
* @param string $enclosure
* @param string $escape
* @param bool $hasHeaders
*/
public function __construct($filename, $delimiter = ',', $enclosure = '"', $escape = '\\', $hasHeaders = true)
{
$this->filename = $filename;
$this->delimiter = $delimiter;
$this->enclosure = $enclosure;
$this->escape = $escape;
$this->hasHeaders = $hasHeaders;
}
/**
* {@inheritdoc}
*/
public function current()
{
return $this->currentLine;
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->position;
}
/**
* {@inheritdoc}
*/
public function next()
{
$line = fgetcsv($this->file, 0, $this->delimiter, $this->enclosure, $this->escape);
$this->currentLine = $line;
++$this->position;
if ($this->hasHeaders && \is_array($line)) {
$data = [];
foreach ($line as $key => $value) {
$data[$this->columns[$key]] = $value;
}
$this->currentLine = $data;
}
}
/**
* {@inheritdoc}
*/
public function rewind()
{
$this->file = fopen($this->filename, 'r');
$this->position = 0;
$line = fgetcsv($this->file, 0, $this->delimiter, $this->enclosure, $this->escape);
if ($this->hasHeaders) {
$this->columns = $line;
$line = fgetcsv($this->file, 0, $this->delimiter, $this->enclosure, $this->escape);
}
$this->currentLine = $line;
if ($this->hasHeaders && \is_array($line)) {
$data = [];
foreach ($line as $key => $value) {
$data[$this->columns[$key]] = $value;
}
$this->currentLine = $data;
}
}
/**
* {@inheritdoc}
*/
public function valid()
{
if (!\is_array($this->currentLine)) {
if (\is_resource($this->file)) {
fclose($this->file);
}
return false;
}
return true;
}
}
class_exists(\Exporter\Source\CsvSourceIterator::class);

View File

@@ -0,0 +1,113 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Statement;
use Sonata\Exporter\Exception\InvalidMethodCallException;
class DoctrineDBALConnectionSourceIterator implements SourceIteratorInterface
{
/**
* @var Connection
*/
protected $connection;
/**
* @var string
*/
protected $query;
/**
* @var array
*/
protected $parameters;
/**
* @var mixed
*/
protected $current;
/**
* @var int
*/
protected $position;
/**
* @var Statement
*/
protected $statement;
/**
* @param Connection $connection
* @param $query
* @param array $parameters
*/
public function __construct(Connection $connection, $query, array $parameters = [])
{
$this->connection = $connection;
$this->query = $query;
$this->parameters = $parameters;
$this->position = 0;
}
/**
* {@inheritdoc}
*/
public function current()
{
return $this->current;
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->current = $this->statement->fetch(\PDO::FETCH_ASSOC);
++$this->position;
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->position;
}
/**
* {@inheritdoc}
*/
public function valid()
{
return \is_array($this->current);
}
/**
* {@inheritdoc}
*/
public function rewind()
{
if ($this->statement) {
throw new InvalidMethodCallException('Cannot rewind a PDOStatement');
}
$this->statement = $this->connection->prepare($this->query);
$this->statement->execute($this->parameters);
$this->next();
}
}
class_exists(\Exporter\Source\DoctrineDBALConnectionSourceIterator::class);

View File

@@ -0,0 +1,160 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
use Doctrine\ODM\MongoDB\Query\Query;
use Doctrine\ORM\Internal\Hydration\IterableResult;
use Sonata\Exporter\Exception\InvalidMethodCallException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyPath;
class DoctrineODMQuerySourceIterator implements SourceIteratorInterface
{
/**
* @var Query
*/
protected $query;
/**
* @var IterableResult
*/
protected $iterator;
/**
* @var array
*/
protected $propertyPaths;
/**
* @var PropertyAccess
*/
protected $propertyAccessor;
/**
* @var string default DateTime format
*/
protected $dateTimeFormat;
/**
* @param Query $query The Doctrine Query
* @param array $fields Fields to export
* @param string $dateTimeFormat
*/
public function __construct(Query $query, array $fields, $dateTimeFormat = 'r')
{
$this->query = clone $query;
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
$this->propertyPaths = [];
foreach ($fields as $name => $field) {
if (\is_string($name) && \is_string($field)) {
$this->propertyPaths[$name] = new PropertyPath($field);
} else {
$this->propertyPaths[$field] = new PropertyPath($field);
}
}
$this->dateTimeFormat = $dateTimeFormat;
}
/**
* {@inheritdoc}
*/
public function current()
{
$current = $this->iterator->current();
$data = [];
foreach ($this->propertyPaths as $name => $propertyPath) {
$data[$name] = $this->getValue($this->propertyAccessor->getValue($current, $propertyPath));
}
$this->query->getDocumentManager()->getUnitOfWork()->detach($current);
return $data;
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->iterator->next();
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->iterator->key();
}
/**
* {@inheritdoc}
*/
public function valid()
{
return $this->iterator->valid();
}
/**
* {@inheritdoc}
*/
public function rewind()
{
if ($this->iterator) {
throw new InvalidMethodCallException('Cannot rewind a Doctrine\ODM\Query');
}
$this->iterator = $this->query->iterate();
$this->iterator->rewind();
}
/**
* @param string $dateTimeFormat
*/
public function setDateTimeFormat($dateTimeFormat)
{
$this->dateTimeFormat = $dateTimeFormat;
}
/**
* @return string
*/
public function getDateTimeFormat()
{
return $this->dateTimeFormat;
}
/**
* @param $value
*
* @return string|null
*/
protected function getValue($value)
{
if (\is_array($value) || $value instanceof \Traversable) {
$value = null;
} elseif ($value instanceof \DateTimeInterface) {
$value = $value->format($this->dateTimeFormat);
} elseif (\is_object($value)) {
$value = (string) $value;
}
return $value;
}
}
class_exists(\Exporter\Source\DoctrineODMQuerySourceIterator::class);

View File

@@ -0,0 +1,210 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
use Doctrine\ORM\Query;
use Sonata\Exporter\Exception\InvalidMethodCallException;
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\PropertyAccess\PropertyPath;
class DoctrineORMQuerySourceIterator implements SourceIteratorInterface
{
const DATE_PARTS = [
'y' => 'Y',
'm' => 'M',
'd' => 'D',
];
const TIME_PARTS = [
'h' => 'H',
'i' => 'M',
's' => 'S',
];
/**
* @var \Doctrine\ORM\Query
*/
protected $query;
/**
* @var \Doctrine\ORM\Internal\Hydration\IterableResult
*/
protected $iterator;
/**
* @var array
*/
protected $propertyPaths;
/**
* @var PropertyAccessor
*/
protected $propertyAccessor;
/**
* @var string default DateTime format
*/
protected $dateTimeFormat;
/**
* @param \Doctrine\ORM\Query $query The Doctrine Query
* @param array $fields Fields to export
* @param string $dateTimeFormat
*/
public function __construct(Query $query, array $fields, $dateTimeFormat = 'r')
{
$this->query = clone $query;
$this->query->setParameters($query->getParameters());
foreach ($query->getHints() as $name => $value) {
$this->query->setHint($name, $value);
}
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
$this->propertyPaths = [];
foreach ($fields as $name => $field) {
if (\is_string($name) && \is_string($field)) {
$this->propertyPaths[$name] = new PropertyPath($field);
} else {
$this->propertyPaths[$field] = new PropertyPath($field);
}
}
$this->dateTimeFormat = $dateTimeFormat;
}
/**
* {@inheritdoc}
*/
public function current()
{
$current = $this->iterator->current();
$data = [];
foreach ($this->propertyPaths as $name => $propertyPath) {
try {
$data[$name] = $this->getValue($this->propertyAccessor->getValue($current[0], $propertyPath));
} catch (UnexpectedTypeException $e) {
//non existent object in path will be ignored
$data[$name] = null;
}
}
$this->query->getEntityManager()->getUnitOfWork()->detach($current[0]);
return $data;
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->iterator->next();
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->iterator->key();
}
/**
* {@inheritdoc}
*/
public function valid()
{
return $this->iterator->valid();
}
/**
* {@inheritdoc}
*/
public function rewind()
{
if ($this->iterator) {
throw new InvalidMethodCallException('Cannot rewind a Doctrine\ORM\Query');
}
$this->iterator = $this->query->iterate();
$this->iterator->rewind();
}
/**
* @param string $dateTimeFormat
*/
public function setDateTimeFormat($dateTimeFormat)
{
$this->dateTimeFormat = $dateTimeFormat;
}
/**
* @return string
*/
public function getDateTimeFormat()
{
return $this->dateTimeFormat;
}
/**
* @param \DateInterval $interval
*
* @return string An ISO8601 duration
*/
public function getDuration(\DateInterval $interval)
{
$datePart = '';
foreach (self::DATE_PARTS as $datePartAttribute => $datePartAttributeString) {
if ($interval->$datePartAttribute !== 0) {
$datePart .= $interval->$datePartAttribute.$datePartAttributeString;
}
}
$timePart = '';
foreach (self::TIME_PARTS as $timePartAttribute => $timePartAttributeString) {
if ($interval->$timePartAttribute !== 0) {
$timePart .= $interval->$timePartAttribute.$timePartAttributeString;
}
}
if ('' === $datePart && '' === $timePart) {
return 'P0Y';
}
return 'P'.$datePart.('' !== $timePart ? 'T'.$timePart : '');
}
/**
* @param $value
*
* @return string|null
*/
protected function getValue($value)
{
if (\is_array($value) || $value instanceof \Traversable) {
$value = null;
} elseif ($value instanceof \DateTimeInterface) {
$value = $value->format($this->dateTimeFormat);
} elseif ($value instanceof \DateInterval) {
$value = $this->getDuration($value);
} elseif (\is_object($value)) {
$value = (string) $value;
}
return $value;
}
}
class_exists(\Exporter\Source\DoctrineORMQuerySourceIterator::class);

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
/**
* IteratorCallbackSource is IteratorSource with callback executed each row.
*
* @author Florent Denis <fdenis@ekino.com>
*/
class IteratorCallbackSourceIterator extends IteratorSourceIterator
{
/**
* @var \Closure
*/
protected $transformer;
/**
* @param \Iterator $iterator Iterator with string array elements
* @param \Closure $transformer Altering a data row
*/
public function __construct(\Iterator $iterator, \Closure $transformer)
{
parent::__construct($iterator);
$this->transformer = $transformer;
}
/**
* {@inheritdoc}
*/
public function current()
{
return \call_user_func($this->transformer, $this->iterator->current());
}
}
class_exists(\Exporter\Source\IteratorCallbackSourceIterator::class);

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
/**
* SourceIterator implementation based on Iterator.
*/
class IteratorSourceIterator implements SourceIteratorInterface
{
/**
* @var \Iterator
*/
protected $iterator;
/**
* @param \Iterator $iterator Iterator with string array elements
*/
public function __construct(\Iterator $iterator)
{
$this->iterator = $iterator;
}
/**
* @return \Iterator
*/
public function getIterator()
{
return $this->iterator;
}
/**
* {@inheritdoc}
*/
public function current()
{
return $this->iterator->current();
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->iterator->next();
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->iterator->key();
}
/**
* {@inheritdoc}
*/
public function valid()
{
return $this->iterator->valid();
}
/**
* {@inheritdoc}
*/
public function rewind()
{
$this->iterator->rewind();
}
}
class_exists(\Exporter\Source\IteratorSourceIterator::class);

View File

@@ -0,0 +1,95 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
use Sonata\Exporter\Exception\InvalidMethodCallException;
class PDOStatementSourceIterator implements SourceIteratorInterface
{
/**
* @var \PDOStatement
*/
protected $statement;
/**
* @var mixed
*/
protected $current;
/**
* @var int
*/
protected $position;
/**
* @var bool
*/
protected $rewinded;
/**
* @param \PDOStatement $statement
*/
public function __construct(\PDOStatement $statement)
{
$this->statement = $statement;
$this->position = 0;
$this->rewinded = false;
}
/**
* {@inheritdoc}
*/
public function current()
{
return $this->current;
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->current = $this->statement->fetch(\PDO::FETCH_ASSOC);
++$this->position;
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->position;
}
/**
* {@inheritdoc}
*/
public function valid()
{
return \is_array($this->current);
}
/**
* {@inheritdoc}
*/
public function rewind()
{
if ($this->rewinded) {
throw new InvalidMethodCallException('Cannot rewind a PDOStatement');
}
$this->current = $this->statement->fetch(\PDO::FETCH_ASSOC);
$this->rewinded = true;
}
}
class_exists(\Exporter\Source\PDOStatementSourceIterator::class);

View File

@@ -0,0 +1,163 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
use PropelCollection;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\PropertyAccess\PropertyPath;
/**
* Read data from a PropelCollection.
*
* @author Kévin Gomez <contact@kevingomez.fr>
*/
class PropelCollectionSourceIterator implements SourceIteratorInterface
{
/**
* @var \PropelCollection
*/
protected $collection;
/**
* @var \ArrayIterator
*/
protected $iterator;
/**
* @var array
*/
protected $propertyPaths;
/**
* @var PropertyAccessor
*/
protected $propertyAccessor;
/**
* @var string default DateTime format
*/
protected $dateTimeFormat;
/**
* @param PropelCollection $collection
* @param array $fields Fields to export
* @param string $dateTimeFormat
*/
public function __construct(PropelCollection $collection, array $fields, $dateTimeFormat = 'r')
{
$this->collection = clone $collection;
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
$this->propertyPaths = [];
foreach ($fields as $name => $field) {
if (\is_string($name) && \is_string($field)) {
$this->propertyPaths[$name] = new PropertyPath($field);
} else {
$this->propertyPaths[$field] = new PropertyPath($field);
}
}
$this->dateTimeFormat = $dateTimeFormat;
}
/**
* {@inheritdoc}
*/
public function current()
{
$current = $this->iterator->current();
$data = [];
foreach ($this->propertyPaths as $name => $propertyPath) {
$data[$name] = $this->getValue($this->propertyAccessor->getValue($current, $propertyPath));
}
return $data;
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->iterator->next();
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->iterator->key();
}
/**
* {@inheritdoc}
*/
public function valid()
{
return $this->iterator->valid();
}
/**
* {@inheritdoc}
*/
public function rewind()
{
if ($this->iterator) {
$this->iterator->rewind();
return;
}
$this->iterator = $this->collection->getIterator();
$this->iterator->rewind();
}
/**
* @param string $dateTimeFormat
*/
public function setDateTimeFormat($dateTimeFormat)
{
$this->dateTimeFormat = $dateTimeFormat;
}
/**
* @return string
*/
public function getDateTimeFormat()
{
return $this->dateTimeFormat;
}
/**
* @param $value
*
* @return string|null
*/
protected function getValue($value)
{
if (\is_array($value) || $value instanceof \Traversable) {
$value = null;
} elseif ($value instanceof \DateTimeInterface) {
$value = $value->format($this->dateTimeFormat);
} elseif (\is_object($value)) {
$value = (string) $value;
}
return $value;
}
}
class_exists(\Exporter\Source\PropelCollectionSourceIterator::class);

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
interface SourceIteratorInterface extends \Iterator
{
}
interface_exists(\Exporter\Source\SourceIteratorInterface::class);

View File

@@ -0,0 +1,102 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
class SymfonySitemapSourceIterator implements SourceIteratorInterface
{
/**
* @var RouterInterface
*/
protected $router;
/**
* @var SourceIteratorInterface
*/
protected $source;
/**
* @var string
*/
protected $routeName;
/**
* @var array
*/
protected $parameters;
/**
* @param SourceIteratorInterface $source
* @param RouterInterface $router
* @param string $routeName
* @param array $parameters
*/
public function __construct(SourceIteratorInterface $source, RouterInterface $router, $routeName, array $parameters = [])
{
$this->source = $source;
$this->router = $router;
$this->routeName = $routeName;
$this->parameters = $parameters;
}
/**
* {@inheritdoc}
*/
public function current()
{
$data = $this->source->current();
$parameters = array_merge($this->parameters, array_intersect_key($data, $this->parameters));
if (!isset($data['url'])) {
$data['url'] = $this->router->generate($this->routeName, $parameters, UrlGeneratorInterface::ABSOLUTE_URL);
}
return $data;
}
/**
* {@inheritdoc}
*/
public function next()
{
$this->source->next();
}
/**
* {@inheritdoc}
*/
public function key()
{
return $this->source->key();
}
/**
* {@inheritdoc}
*/
public function valid()
{
return $this->source->valid();
}
/**
* {@inheritdoc}
*/
public function rewind()
{
$this->source->rewind();
}
}
class_exists(\Exporter\Source\SymfonySitemapSourceIterator::class);

View File

@@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
/**
* Read data from a Xml Excel file.
*
* @author Vincent Touzet <vincent.touzet@gmail.com>
*/
class XmlExcelSourceIterator extends AbstractXmlSourceIterator
{
/**
* @param string $filename
* @param bool $hasHeaders
*/
public function __construct($filename, $hasHeaders = true)
{
parent::__construct($filename, $hasHeaders);
}
/**
* {@inheritdoc}
*/
public function tagStart($parser, $name, $attributes = [])
{
switch ($name) {
case 'ss:Row':
case 'Row':
$this->bufferedRow['i_'.$this->currentRowIndex] = [];
break;
case 'ss:Cell':
case 'Cell':
// set empty values when opening Cell tag
$this->bufferedRow['i_'.$this->currentRowIndex][$this->currentColumnIndex] = '';
break;
}
}
/**
* {@inheritdoc}
*/
public function tagEnd($parser, $name)
{
switch ($name) {
case 'ss:Row':
case 'Row':
$this->currentRowIndex++;
$this->currentColumnIndex = 0;
$this->currentRowEnded = true;
break;
case 'ss:Cell':
case 'Cell':
$this->currentColumnIndex++;
break;
}
}
/**
* {@inheritdoc}
*/
public function tagContent($parser, $data)
{
$this->bufferedRow['i_'.$this->currentRowIndex][$this->currentColumnIndex] .= $data;
}
}
class_exists(\Exporter\Source\XmlExcelSourceIterator::class);

View File

@@ -0,0 +1,114 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Source;
/**
* Read data from a Xml file.
*
* @author Vincent Touzet <vincent.touzet@gmail.com>
*/
class XmlSourceIterator extends AbstractXmlSourceIterator
{
/**
* @var string
*/
protected $mainTag;
/**
* @var string
*/
protected $dataTag;
/**
* @param string $filename
* @param string $mainTag
* @param string $dataTag
*/
public function __construct($filename, $mainTag = 'datas', $dataTag = 'data')
{
parent::__construct($filename, false);
$this->mainTag = $mainTag;
$this->dataTag = $dataTag;
}
/**
* {@inheritdoc}
*/
public function tagStart($parser, $name, $attributes = [])
{
switch ($name) {
case $this->mainTag:
break;
case $this->dataTag:
$this->bufferedRow['i_'.$this->currentRowIndex] = [];
break;
default:
if (!isset($this->columns[$this->currentColumnIndex])) {
$this->columns[$this->currentColumnIndex] = $name;
}
// set empty values when opening Cell tag
$this->bufferedRow['i_'.$this->currentRowIndex][$this->currentColumnIndex] = '';
break;
}
}
/**
* {@inheritdoc}
*/
public function tagEnd($parser, $name)
{
switch ($name) {
case $this->mainTag:
break;
case $this->dataTag:
$this->currentRowIndex++;
$this->currentColumnIndex = 0;
$this->currentRowEnded = true;
break;
default:
$this->currentColumnIndex++;
break;
}
}
/**
* {@inheritdoc}
*/
public function tagContent($parser, $data)
{
if (isset($this->bufferedRow['i_'.$this->currentRowIndex], $this->bufferedRow['i_'.$this->currentRowIndex][$this->currentColumnIndex])
) {
$this->bufferedRow['i_'.$this->currentRowIndex][$this->currentColumnIndex] .= $data;
}
}
/**
* {@inheritdoc}
*/
protected function prepareCurrentRow()
{
$this->currentRow = array_shift($this->bufferedRow);
if (\is_array($this->currentRow)) {
$datas = [];
foreach ($this->currentRow as $key => $value) {
$datas[$this->columns[$key]] = $value;
}
$this->currentRow = $datas;
}
}
}
class_exists(\Exporter\Source\XmlSourceIterator::class);

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Test;
/**
* @author Grégoire Paris <postmaster@greg0ire.fr>
*/
abstract class AbstractTypedWriterTestCase extends \PHPUnit\Framework\TestCase
{
/**
* @var WriterInterface
*/
private $writer;
protected function setUp()
{
$this->writer = $this->getWriter();
}
public function testFormatIsString()
{
$this->assertInternalType('string', $this->writer->getFormat());
}
public function testDefaultMimeTypeIsString()
{
$this->assertInternalType('string', $this->writer->getDefaultMimeType());
}
/**
* Should return a very simple instance of the writer (no need for complex
* configuration).
*
* @return WriterInterface
*/
abstract protected function getWriter();
}
class_exists(\Exporter\Test\AbstractTypedWriterTestCase::class);

View File

@@ -0,0 +1,171 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
use Sonata\Exporter\Exception\InvalidDataFormatException;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class CsvWriter implements TypedWriterInterface
{
/**
* @var string
*/
protected $filename;
/**
* @var string
*/
protected $delimiter;
/**
* @var string
*/
protected $enclosure;
/**
* @var string
*/
protected $escape;
/**
* @var resource
*/
protected $file;
/**
* @var bool
*/
protected $showHeaders;
/**
* @var int
*/
protected $position;
/**
* @var bool
*/
protected $withBom;
/**
* @var string
*/
private $terminate;
/**
* @param string $filename
* @param string $delimiter
* @param string $enclosure
* @param string $escape
* @param bool $showHeaders
* @param bool $withBom
* @param string $terminate
*/
public function __construct(
$filename,
$delimiter = ',',
$enclosure = '"',
$escape = '\\',
$showHeaders = true,
$withBom = false,
$terminate = "\n"
) {
$this->filename = $filename;
$this->delimiter = $delimiter;
$this->enclosure = $enclosure;
$this->escape = $escape;
$this->showHeaders = $showHeaders;
$this->terminate = $terminate;
$this->position = 0;
$this->withBom = $withBom;
if (is_file($filename)) {
throw new \RuntimeException(sprintf('The file %s already exist', $filename));
}
}
/**
* {@inheritdoc}
*/
final public function getDefaultMimeType()
{
return 'text/csv';
}
/**
* {@inheritdoc}
*/
final public function getFormat()
{
return 'csv';
}
/**
* {@inheritdoc}
*/
public function open()
{
$this->file = fopen($this->filename, 'w', false);
if ("\n" !== $this->terminate) {
stream_filter_register('filterTerminate', CsvWriterTerminate::class);
stream_filter_append($this->file, 'filterTerminate', STREAM_FILTER_WRITE, ['terminate' => $this->terminate]);
}
if (true === $this->withBom) {
fprintf($this->file, \chr(0xEF).\chr(0xBB).\chr(0xBF));
}
}
/**
* {@inheritdoc}
*/
public function close()
{
fclose($this->file);
}
/**
* {@inheritdoc}
*/
public function write(array $data)
{
if (0 == $this->position && $this->showHeaders) {
$this->addHeaders($data);
++$this->position;
}
$result = @fputcsv($this->file, $data, $this->delimiter, $this->enclosure, $this->escape);
if (!$result) {
throw new InvalidDataFormatException();
}
++$this->position;
}
/**
* @param array $data
*/
protected function addHeaders(array $data)
{
$headers = [];
foreach ($data as $header => $value) {
$headers[] = $header;
}
fputcsv($this->file, $headers, $this->delimiter, $this->enclosure, $this->escape);
}
}
class_exists(\Exporter\Writer\CsvWriter::class);

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
/**
* Filter CSV output to replace the default terminator while supporting active streams.
*/
final class CsvWriterTerminate extends \php_user_filter
{
/**
* @param $in
* @param $out
* @param $consumed
* @param $closing
*
* @return int
*/
public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
if (isset($this->params['terminate'])) {
$bucket->data = preg_replace('/([^\r])\n/', '$1'.$this->params['terminate'], $bucket->data);
}
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
class_exists(\Exporter\Writer\CsvWriterTerminate::class);

View File

@@ -0,0 +1,78 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
/**
* Format boolean before use another writer.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class FormattedBoolWriter implements WriterInterface
{
/**
* @var WriterInterface
*/
protected $writer;
/**
* @var string
*/
protected $trueLabel;
/**
* @var string
*/
protected $falseLabel;
/**
* @param WriterInterface $writer
* @param string $falseLabel
* @param string $trueLabel
*/
public function __construct(WriterInterface $writer, $trueLabel = 'yes', $falseLabel = 'no')
{
$this->writer = $writer;
$this->trueLabel = $trueLabel;
$this->falseLabel = $falseLabel;
}
/**
* {@inheritdoc}
*/
public function open()
{
$this->writer->open();
}
/**
* {@inheritdoc}
*/
public function close()
{
$this->writer->close();
}
/**
* {@inheritdoc}
*/
public function write(array $data)
{
foreach ($data as $key => $value) {
if (\is_bool($data[$key])) {
$data[$key] = true === $data[$key] ? $this->trueLabel : $this->falseLabel;
}
}
$this->writer->write($data);
}
}
class_exists(\Exporter\Writer\FormattedBoolWriter::class);

View File

@@ -0,0 +1,161 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
/**
* Generates a GSA feed.
*
* @author Rémi Marseille <marseille@ekino.com>
*/
class GsaFeedWriter implements WriterInterface
{
const LIMIT_SIZE = 31457280; // 30MB
/**
* @var \SplFileInfo
*/
private $folder;
/**
* @var string
*/
private $dtd;
/**
* @var string
*/
private $datasource;
/**
* @var string
*/
private $feedtype;
/**
* @var int
*/
private $bufferPart;
/**
* @var resource
*/
private $buffer;
/**
* @var int
*/
private $bufferSize;
/**
* @param \SplFileInfo $folder The folder to store the generated feed(s)
* @param string $dtd A DTD URL (something like http://gsa.example.com/gsafeed.dtd)
* @param string $datasource A datasouce
* @param string $feedtype A feedtype (full|incremental|metadata-and-url)
*/
public function __construct(\SplFileInfo $folder, $dtd, $datasource, $feedtype)
{
$this->folder = $folder;
$this->dtd = $dtd;
$this->datasource = $datasource;
$this->feedtype = $feedtype;
$this->bufferPart = 0;
$this->bufferSize = 0;
}
/**
* {@inheritdoc}
*/
public function open()
{
$this->generateNewPart();
}
/**
* {@inheritdoc}
*/
public function write(array $data)
{
$line = sprintf(" <record url=\"%s\" mimetype=\"%s\" action=\"%s\"/>\n",
$data['url'],
$data['mime_type'],
$data['action']
);
// + 18 corresponding to the length of the closing tags
if (($this->bufferSize + \strlen($line) + 18) > self::LIMIT_SIZE) {
$this->generateNewPart();
}
$this->bufferSize += fwrite($this->buffer, $line);
}
/**
* {@inheritdoc}
*/
public function close()
{
if ($this->buffer) {
$this->closeFeed();
}
}
/**
* Generates a new file.
*
* @throws \RuntimeException
*/
private function generateNewPart()
{
if ($this->buffer) {
$this->closeFeed();
}
$this->bufferSize = 0;
++$this->bufferPart;
if (!is_writable($this->folder)) {
throw new \RuntimeException(sprintf('Unable to write to folder: %s', $this->folder));
}
$this->buffer = fopen(sprintf('%s/feed_%05d.xml', $this->folder, $this->bufferPart), 'w');
$this->bufferSize += fwrite($this->buffer, <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE gsafeed PUBLIC "-//Google//DTD GSA Feeds//EN" "$this->dtd">
<gsafeed>
<header>
<datasource>$this->datasource</datasource>
<feedtype>$this->feedtype</feedtype>
</header>
<group>
XML
);
}
/**
* Closes the current feed.
*/
private function closeFeed()
{
fwrite($this->buffer, <<<'EOF'
</group>
</gsafeed>
EOF
);
fclose($this->buffer);
}
}
class_exists(\Exporter\Writer\GsaFeedWriter::class);

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
class InMemoryWriter implements WriterInterface
{
/**
* @var array
*/
protected $elements;
/**
* {@inheritdoc}
*/
public function open()
{
$this->elements = [];
}
/**
* {@inheritdoc}
*/
public function close()
{
return $this->elements;
}
/**
* {@inheritdoc}
*/
public function write(array $data)
{
$this->elements[] = $data;
}
/**
* @return array
*/
public function getElements()
{
return $this->elements;
}
}
class_exists(\Exporter\Writer\InMemoryWriter::class);

View File

@@ -0,0 +1,94 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class JsonWriter implements TypedWriterInterface
{
/**
* @var string
*/
protected $filename;
/**
* @var resource
*/
protected $file;
/**
* @var int
*/
protected $position;
/**
* @param string $filename
*/
public function __construct($filename)
{
$this->filename = $filename;
$this->position = 0;
if (is_file($filename)) {
throw new \RuntimeException(sprintf('The file %s already exist', $filename));
}
}
/**
* {@inheritdoc}
*/
final public function getDefaultMimeType()
{
return 'application/json';
}
/**
* {@inheritdoc}
*/
final public function getFormat()
{
return 'json';
}
/**
* {@inheritdoc}
*/
public function open()
{
$this->file = fopen($this->filename, 'w', false);
fwrite($this->file, '[');
}
/**
* {@inheritdoc}
*/
public function close()
{
fwrite($this->file, ']');
fclose($this->file);
}
/**
* {@inheritdoc}
*/
public function write(array $data)
{
fwrite($this->file, ($this->position > 0 ? ',' : '').json_encode($data));
++$this->position;
}
}
class_exists(\Exporter\Writer\JsonWriter::class);

View File

@@ -0,0 +1,369 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
/**
* Generates a sitemap site from.
*/
class SitemapWriter implements WriterInterface
{
const LIMIT_SIZE = 10485760;
const LIMIT_URL = 50000;
/**
* @var string
*/
protected $folder;
/**
* @var string
*/
protected $pattern;
/**
* @var string
*/
protected $groupName;
/**
* @var bool
*/
protected $autoIndex;
/**
* @var resource
*/
protected $buffer;
/**
* @var array
*/
protected $headers;
/**
* @var int
*/
protected $bufferSize = 0;
/**
* @var int
*/
protected $bufferUrlCount = 0;
/**
* @var int
*/
protected $bufferPart = 0;
/**
* @param string $folder The folder to store the sitemap.xml file
* @param mixed $groupName Name of sub-sitemap (optional)
* @param array $headers Indicate the need for namespace in the header sitemap
* @param bool $autoIndex If you want to generate index of sitemap (optional)
*/
public function __construct($folder, $groupName = false, array $headers = [], $autoIndex = true)
{
$this->folder = $folder;
$this->groupName = \is_string($groupName) ? $groupName : '';
$this->headers = $headers;
$this->autoIndex = $autoIndex;
$this->pattern = 'sitemap_'.($this->groupName ? $this->groupName.'_' : '').'%05d.xml';
}
/**
* Returns the status of auto generation of index site map.
*
* @return bool
*/
public function isAutoIndex()
{
return $this->autoIndex;
}
/**
* Returns folder to store the sitemap.xml file.
*
* @return string
*/
public function getFolder()
{
return $this->folder;
}
/**
* {@inheritdoc}
*/
public function open()
{
$this->bufferPart = 0;
$this->generateNewPart();
}
/**
* {@inheritdoc}
*/
public function write(array $data)
{
$data = $this->buildData($data);
switch ($data['type']) {
case 'video':
$line = $this->generateVideoLine($data);
break;
case 'image':
$line = $this->generateImageLine($data);
break;
case 'default':
default:
$line = $this->generateDefaultLine($data);
}
$this->addSitemapLine($line);
}
/**
* {@inheritdoc}
*/
public function close()
{
if ($this->buffer) {
$this->closeSitemap();
}
if ($this->autoIndex) {
self::generateSitemapIndex(
$this->folder,
'sitemap_'.($this->groupName ? $this->groupName.'_' : '').'*.xml',
'sitemap'.($this->groupName ? '_'.$this->groupName : '').'.xml'
);
}
}
/**
* Generates the sitemap index from the sitemap part avaible in the folder.
*
* @param string $folder A folder to write sitemap index
* @param string $baseUrl A base URL
* @param string $pattern A sitemap pattern, optional
* @param string $filename A sitemap file name, optional
*/
public static function generateSitemapIndex($folder, $baseUrl, $pattern = 'sitemap*.xml', $filename = 'sitemap.xml')
{
$content = "<?xml version='1.0' encoding='UTF-8'?".">\n<sitemapindex xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://www.sitemaps.org/schemas/sitemap/1.0 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd' xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'>\n";
foreach (glob(sprintf('%s/%s', $folder, $pattern)) as $file) {
$stat = stat($file);
$content .= sprintf("\t".'<sitemap><loc>%s/%s</loc><lastmod>%s</lastmod></sitemap>'."\n",
$baseUrl,
basename($file),
date('Y-m-d', $stat['mtime'])
);
}
$content .= '</sitemapindex>';
file_put_contents(sprintf('%s/%s', $folder, $filename), $content);
}
/**
* Generate a new sitemap part.
*
* @throws \RuntimeException
*/
protected function generateNewPart()
{
if ($this->buffer) {
$this->closeSitemap();
}
$this->bufferUrlCount = 0;
$this->bufferSize = 0;
++$this->bufferPart;
if (!is_writable($this->folder)) {
throw new \RuntimeException(sprintf('Unable to write to folder: %s', $this->folder));
}
$filename = sprintf($this->pattern, $this->bufferPart);
$this->buffer = fopen($this->folder.'/'.$filename, 'w');
$this->bufferSize += fwrite($this->buffer, '<?xml version="1.0" encoding="UTF-8"?>'."\n".'<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"'.$this->getHeaderByFlag().'>'."\n");
}
/**
* Add a new line into the sitemap part.
*
* @param string $line
*/
protected function addSitemapLine($line)
{
if ($this->bufferUrlCount >= self::LIMIT_URL) {
$this->generateNewPart();
}
if (($this->bufferSize + \strlen($line) + 9) > self::LIMIT_SIZE) {
$this->generateNewPart();
}
++$this->bufferUrlCount;
$this->bufferSize += fwrite($this->buffer, $line);
}
/**
* Build data with default parameters.
*
* @param array $data List of parameters
*
* @return array
*/
protected function buildData(array $data)
{
$default = [
'url' => null,
'lastmod' => 'now',
'changefreq' => 'weekly',
'priority' => 0.5,
'type' => 'default',
];
$data = array_merge($default, $data);
$this->fixDataType($data);
return $data;
}
/**
* Fix type of data, if data type is specific,
* he must to be defined in data and he must to be a array.
*
* @param array &$data List of parameters
*/
protected function fixDataType(array &$data)
{
if ('default' === $data['type']) {
return;
}
$valid_var_name = [
'image' => 'images',
'video' => 'video',
];
if (!isset($valid_var_name[$data['type']], $data[$valid_var_name[$data['type']]]) || !\is_array($data[$valid_var_name[$data['type']]])) {
$data['type'] = 'default';
}
}
/**
* Generate standard line of sitemap.
*
* @param array $data List of parameters
*
* @return string
*/
protected function generateDefaultLine(array $data)
{
return sprintf(' '.'<url><loc>%s</loc><lastmod>%s</lastmod><changefreq>%s</changefreq><priority>%s</priority></url>'."\n", $data['url'], date('Y-m-d', strtotime($data['lastmod'])), $data['changefreq'], $data['priority']);
}
/**
* Generate image line of sitemap.
*
* @param array $data List of parameters
*
* @return string
*/
protected function generateImageLine(array $data)
{
$images = '';
if (\count($data['images']) > 1000) {
$data['images'] = array_splice($data['images'], 1000);
}
$builder = [
'url' => 'loc',
'location' => 'geo_location',
];
foreach ($data['images'] as $image) {
$images .= '<image:image>';
foreach ($image as $key => $element) {
$images .= sprintf('<image:%1$s>%2$s</image:%1$s>', (isset($builder[$key]) ? $builder[$key] : $key), $element);
}
$images .= '</image:image>';
}
return sprintf(' '.'<url><loc>%s</loc>%s</url>'."\n", $data['url'], $images);
}
/**
* Generate video line of sitemap.
*
* @param array $data List of parameters
*
* @return string
*/
protected function generateVideoLine(array $data)
{
$videos = '';
$builder = [
'thumbnail' => 'thumbnail_loc',
];
foreach ($data['video'] as $key => $video) {
$videos .= sprintf('<video:%1$s>%2$s</video:%1$s>', (isset($builder[$key]) ? $builder[$key] : $key), $video);
}
return sprintf(' '.'<url><loc>%s</loc><video:video>%s</video:video></url>'."\n", $data['url'], $videos);
}
/**
* Generate additional header with namespace adapted to the content.
*
* @return string
*/
protected function getHeaderByFlag()
{
$namespaces = [
'video' => 'xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"',
'image' => 'xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"',
];
$result = '';
foreach ($this->headers as $flag) {
$result .= ' '.$namespaces[$flag];
}
return $result;
}
/**
* Close the sitemap part.
*/
protected function closeSitemap()
{
fwrite($this->buffer, '</urlset>');
fclose($this->buffer);
}
}
class_exists(\Exporter\Writer\SitemapWriter::class);

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
/**
* @author Grégoire Paris <postmaster@greg0ire.fr>
*/
interface TypedWriterInterface extends WriterInterface
{
/**
* There can be several mime types for a given format, this method should
* return the most appopriate / popular one.
*
* @return string the mime type of the output
*/
public function getDefaultMimeType();
/**
* Returns a string best describing the format of the output (the file
* extension is fine, for example).
*
* @return string a string without spaces, usable in a translation string
*/
public function getFormat();
}
interface_exists(\Exporter\Writer\TypedWriterInterface::class);

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
interface WriterInterface
{
public function open();
/**
* @param array $data
*/
public function write(array $data);
public function close();
}
interface_exists(\Exporter\Writer\WriterInterface::class);

View File

@@ -0,0 +1,126 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class XlsWriter implements TypedWriterInterface
{
/**
* @var string
*/
protected $filename;
/**
* @var resource
*/
protected $file;
/**
* @var bool
*/
protected $showHeaders;
/**
* @var int
*/
protected $position;
/**
* @param $filename
* @param bool $showHeaders
*
* @throws \RuntimeException
*/
public function __construct($filename, $showHeaders = true)
{
$this->filename = $filename;
$this->showHeaders = $showHeaders;
$this->position = 0;
if (is_file($filename)) {
throw new \RuntimeException(sprintf('The file %s already exist', $filename));
}
}
/**
* {@inheritdoc}
*/
final public function getDefaultMimeType()
{
return 'application/vnd.ms-excel';
}
/**
* {@inheritdoc}
*/
final public function getFormat()
{
return 'xls';
}
/**
* {@inheritdoc}
*/
public function open()
{
$this->file = fopen($this->filename, 'w', false);
fwrite($this->file, '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name=ProgId content=Excel.Sheet><meta name=Generator content="https://github.com/sonata-project/exporter"></head><body><table>');
}
/**
* {@inheritdoc}
*/
public function close()
{
fwrite($this->file, '</table></body></html>');
fclose($this->file);
}
/**
* {@inheritdoc}
*/
public function write(array $data)
{
$this->init($data);
fwrite($this->file, '<tr>');
foreach ($data as $value) {
fwrite($this->file, sprintf('<td>%s</td>', $value));
}
fwrite($this->file, '</tr>');
++$this->position;
}
/**
* @param $data
*/
protected function init($data)
{
if ($this->position > 0) {
return;
}
if ($this->showHeaders) {
fwrite($this->file, '<tr>');
foreach ($data as $header => $value) {
fwrite($this->file, sprintf('<th>%s</th>', $header));
}
fwrite($this->file, '</tr>');
++$this->position;
}
}
}
class_exists(\Exporter\Writer\XlsWriter::class);

View File

@@ -0,0 +1,155 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
/**
* Generate a Xml Excel file.
*
* @author Vincent Touzet <vincent.touzet@gmail.com>
*/
class XmlExcelWriter implements WriterInterface
{
/**
* @var string|null
*/
protected $filename = null;
/**
* @var resource|null
*/
protected $file = null;
/**
* @var bool
*/
protected $showHeaders;
/**
* @var mixed|null
*/
protected $columnsType = null;
/**
* @var int
*/
protected $position = 0;
/**
* @var string
*/
protected $header = '<?xml version="1.0"?><?mso-application progid="Excel.Sheet"?><Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:x2="http://schemas.microsoft.com/office/excel/2003/xml" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:html="http://www.w3.org/TR/REC-html40" xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"><OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office"></OfficeDocumentSettings><ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel"></ExcelWorkbook><Worksheet ss:Name="Sheet 1"><Table>';
protected $footer = '</Table></Worksheet></Workbook>';
/**
* @param string $filename
* @param bool $showHeaders
* @param mixed $columnsType Define cells type to use
* If string: force all cells to the given type. e.g: 'Number'
* If array: force only given cells. e.g: array('ean'=>'String', 'price'=>'Number')
* If null: will guess the type. 'Number' if value is numeric, 'String' otherwise
*/
public function __construct($filename, $showHeaders = true, $columnsType = null)
{
$this->filename = $filename;
$this->showHeaders = $showHeaders;
$this->columnsType = $columnsType;
if (is_file($filename)) {
throw new \RuntimeException(sprintf('The file %s already exist', $filename));
}
}
public function open()
{
$this->file = fopen($this->filename, 'w');
fwrite($this->file, $this->header);
}
/**
* @param array $data
*/
public function write(array $data)
{
if (0 == $this->position && $this->showHeaders) {
$header = array_keys($data);
fwrite($this->file, $this->getXmlString($header));
++$this->position;
}
fwrite($this->file, $this->getXmlString($data));
++$this->position;
}
public function close()
{
fwrite($this->file, $this->footer);
fclose($this->file);
}
/**
* Prepare and return XML string for MS Excel XML from array.
*
* @param array $fields
*
* @return string
*/
private function getXmlString(array $fields = [])
{
$xmlData = [];
$xmlData[] = '<Row>';
foreach ($fields as $key => $value) {
$value = htmlspecialchars($value);
$value = str_replace(["\r\n", "\r", "\n"], '&#10;', $value);
$dataType = 'String';
if (0 != $this->position || !$this->showHeaders) {
$dataType = $this->getDataType($key, $value);
}
$xmlData[] = '<Cell><Data ss:Type="'.$dataType.'">'.$value.'</Data></Cell>';
}
$xmlData[] = '</Row>';
return implode('', $xmlData);
}
/**
* @param string $key
* @param string $value
*
* @return string
*/
private function getDataType($key, $value)
{
$dataType = null;
if (null !== $this->columnsType) {
if (\is_string($this->columnsType)) {
$dataType = $this->columnsType;
} elseif (\is_array($this->columnsType)) {
if (\array_key_exists($key, $this->columnsType)) {
$dataType = $this->columnsType[$key];
}
}
}
if (null === $dataType) {
// guess the type
if (is_numeric($value)) {
$dataType = 'Number';
} else {
$dataType = 'String';
}
}
return $dataType;
}
}
class_exists(\Exporter\Writer\XmlExcelWriter::class);

View File

@@ -0,0 +1,129 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\Exporter\Writer;
use Sonata\Exporter\Exception\InvalidDataFormatException;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class XmlWriter implements TypedWriterInterface
{
/**
* @var string
*/
protected $filename;
/**
* @var resource
*/
protected $file;
/**
* @var int
*/
protected $position;
/**
* @var string
*/
protected $mainElement;
/**
* @var string
*/
protected $childElement;
/**
* @param string $filename
* @param string $mainElement
* @param string $childElement
*/
public function __construct($filename, $mainElement = 'datas', $childElement = 'data')
{
$this->filename = $filename;
$this->position = 0;
$this->mainElement = $mainElement;
$this->childElement = $childElement;
if (is_file($filename)) {
throw new \RuntimeException(sprintf('The file %s already exist', $filename));
}
}
/**
* {@inheritdoc}
*/
final public function getDefaultMimeType()
{
return 'text/xml';
}
/**
* {@inheritdoc}
*/
final public function getFormat()
{
return 'xml';
}
/**
* {@inheritdoc}
*/
public function open()
{
$this->file = fopen($this->filename, 'w', false);
fwrite($this->file, sprintf("<?xml version=\"1.0\" ?>\n<%s>\n", $this->mainElement));
}
/**
* {@inheritdoc}
*/
public function close()
{
fwrite($this->file, sprintf('</%s>', $this->mainElement));
fclose($this->file);
}
/**
* {@inheritdoc}
*/
public function write(array $data)
{
fwrite($this->file, sprintf("<%s>\n", $this->childElement));
foreach ($data as $k => $v) {
$this->generateNode($k, $v);
}
fwrite($this->file, sprintf("</%s>\n", $this->childElement));
}
/**
* @param string $name
* @param string $value
*/
protected function generateNode($name, $value)
{
if (\is_array($value)) {
throw new \RuntimeException('Not implemented');
} elseif (is_scalar($value) || null === $value) {
fwrite($this->file, sprintf("<%s><![CDATA[%s]]></%s>\n", $name, $value, $name));
} else {
throw new InvalidDataFormatException('Invalid data');
}
}
}
class_exists(\Exporter\Writer\XmlWriter::class);