<?php
/**
 * Register, minify and unify CSS and JavaScript resources.
 *
 * @author    Josantonius <hello@josantonius.com>
 * @package   Josantonius\WP_Register
 * @copyright 2017 - 2018 (c) Josantonius - WP_Register
 * @license   https://opensource.org/licenses/MIT - The MIT License (MIT)
 * @link      https://github.com/Josantonius/WP_Register
 * @since     1.0.0
 */

namespace Josantonius\WP_Register;

/**
 * Register CSS and JavaScript resources.
 */
class WP_Register {

	/**
	 * Unique identifier for unified file.
	 *
	 * @since 1.0.4
	 *
	 * @var string
	 */
	protected static $id;

	/**
	 * Settings to register styles or scripts.
	 *
	 * @var array
	 */
	protected static $data = [];

	/**
	 * Files saved information.
	 *
	 * @since 1.0.4
	 *
	 * @var array
	 */
	protected static $files;

	/**
	 * True if file is outdated.
	 *
	 * @since 1.0.4
	 *
	 * @var boolean
	 */
	protected static $changes = false;

	/**
	 * Unify files.
	 *
	 * @since 1.0.4
	 *
	 * @var string|boolean
	 */
	protected static $unify = false;

	/**
	 * Minify files.
	 *
	 * @since 1.0.4
	 *
	 * @var boolean
	 */
	protected static $minify = false;

	/**
	 * Add scripts or styles.
	 *
	 * @param string $type → script|style.
	 * @param array  $data → settings.
	 *
	 * @see https://github.com/Josantonius/WP_Register#add
	 *
	 * @return boolean
	 */
	public static function add( $type, $data = [] ) {

		$is_admin = is_admin();

		if ( self::validate( $type, $data, $is_admin ) ) {

			$hook = $is_admin ? 'admin_enqueue_scripts' : 'wp_enqueue_scripts';

			$method = __CLASS__ . "::add_{$type}s";

			if ( has_action( $hook, $method ) === false ) {
				add_action( $hook, $method );
			}

			return true;
		}

		return false;
	}

	/**
	 * Register, enqueue and localize scripts.
	 *
	 * The parameters "plugin_url" and "nonce" (based on script name) are added to each script.
	 */
	public static function add_scripts() {

		self::look_if_process_files( 'script' );

		foreach ( self::$data['script'] as $data ) {

			$params = [
				'plugin_url' => defined( 'WP_PLUGIN_URL' ) ? WP_PLUGIN_URL . '/' : '',
				'nonce'      => wp_create_nonce( $data['name'] ),
			];

			$data['params'] = array_merge( $data['params'], $params );

			wp_register_script(
				$data['name'],
				$data['url'],
				$data['deps'],
				$data['version'],
				$data['footer']
			);

			wp_enqueue_script( $data['name'] );

			wp_localize_script(
				$data['name'],
				$data['name'],
				$data['params']
			);
		}
	}

	/**
	 * Add register and enqueue styles.
	 */
	public static function add_styles() {

		self::look_if_process_files( 'style' );

		foreach ( self::$data['style'] as $data ) {

			wp_register_style(
				$data['name'],
				$data['url'],
				$data['deps'],
				$data['version'],
				$data['media']
			);

			wp_enqueue_style( $data['name'] );
		}
	}

	/**
	 * Check if a particular style or script has been added to be enqueued.
	 *
	 * @since 1.0.5
	 *
	 * @param string $type → script|style.
	 * @param string $name → script or style name.
	 *
	 * @return boolean
	 */
	public static function is_added( $type, $name ) {

		return isset( self::$data[ $type ][ $name ] );
	}

	/**
	 * Sets whether to merge the content of files into a single file.
	 *
	 * @since 1.0.4
	 *
	 * @param string  $id     → unique identifier for unified file.
	 * @param mixed   $params → path urls.
	 * @param boolean $minify → minimize file content.
	 *
	 * @return boolean true
	 */
	public static function unify( $id, $params, $minify = '' ) {

		self::$id     = $id;
		self::$unify  = $params;
		self::$minify = $minify;

		return true;
	}

	/**
	 * Remove before script or style have been registered.
	 *
	 * @since 1.0.1
	 *
	 * @param string $type → script|style.
	 * @param string $name → script or style name.
	 *
	 * @return boolean true
	 */
	public static function remove( $type, $name ) {

		if ( isset( self::$data[ $type ][ $name ] ) ) {

			unset( self::$data[ $type ][ $name ] );
		}

		return true;
	}

	/**
	 * Check if a particular style or script has been added to be enqueued.
	 *
	 * IMPORTANT: This method will be removed soon, use the 'is_added()' method instead.
	 *
	 * @since 1.0.4
	 *
	 * @deprecated 1.0.5
	 *
	 * @param string $type → script|style.
	 * @param string $name → script or style name.
	 *
	 * @return boolean
	 */
	public static function isAdded( $type, $name ) {
		return self::is_added( $type, $name );
	}

	/**
	 * Validate file register.
	 *
	 * @since 1.0.4
	 *
	 * @param string $type  → script|style.
	 * @param array  $data  → settings.
	 * @param bool   $admin → if is admin.
	 *
	 * @return boolean
	 */
	protected static function validate( $type, $data, $admin ) {

		$place = ( isset( $data['place'] ) ) ? $data['place'] : 'front';

		$place = $admin && 'admin' == $place || ! $admin && 'front' == $place;

		if ( ! $place || self::set_params( $type, $data ) === false ) {
			return false;
		}

		return true;
	}

	/**
	 * Set parameters.
	 *
	 * @since 1.0.4
	 *
	 * @param string $type → script|style.
	 * @param array  $data → settings.
	 *
	 * @return boolean
	 */
	protected static function set_params( $type, $data ) {

		if ( ! isset( $data['name'], $data['url'] ) ) {
			return false;
		}

		$data['deps']    = isset( $data['deps'] ) ? $data['deps'] : [];
		$data['version'] = isset( $data['version'] ) ? $data['version'] : false;

		switch ( $type ) {

			case 'script':
				$data['footer'] = isset( $data['footer'] ) ? $data['footer'] : true;
				$data['params'] = isset( $data['params'] ) ? $data['params'] : [];
				break;

			case 'style':
				$data['media'] = isset( $data['media'] ) ? $data['media'] : '';
				break;

			default:
		}

		self::$data[ $type ][ $data['name'] ] = $data;

		return true;
	}

	/**
	 * Look whether to process files to minify or unify files.
	 *
	 * @since 1.0.4
	 *
	 * @param string $type → script|style.
	 *
	 * @return boolean
	 */
	protected static function look_if_process_files( $type ) {

		if ( is_string( self::$unify ) || isset( self::$unify[ "{$type}s" ] ) ) {
			return self::unify_files(
				self::prepare_files( $type )
			);
		}
	}

	/**
	 * Check files and prepare paths and urls.
	 *
	 * @since 1.0.4
	 *
	 * @param string $type → script|style.
	 *
	 * @return array|false → paths, urls and outdated files.
	 */
	protected static function prepare_files( $type ) {

		$params['type']   = $type;
		$params['routes'] = self::get_routes_to_folder( $type );

		self::get_processed_files();

		foreach ( self::$data[ $type ] as $id => $file ) {

			$path = self::get_path_from_url( $file['url'] );

			$params['files'][ $id ] = basename( $file['url'] );
			$params['urls'][ $id ]  = $file['url'];
			$params['paths'][ $id ] = $path;

			if ( is_file( $path ) && self::is_modified_file( $path ) ) {
				unset( $params['urls'][ $id ] );
				continue;
			}

			$path = $params['routes']['path'] . $params['files'][ $id ];

			if ( is_file( $path ) ) {
				if ( self::is_modified_hash( $file['url'], $path ) ) {
					continue;
				}
				$params['paths'][ $id ] = $path;

			} elseif ( self::is_external_url( $file['url'] ) ) {
				continue;
			}

			unset( $params['urls'][ $id ] );
		}

		return $params;
	}

	/**
	 * Get path|url to the minimized file.
	 *
	 * @since 1.0.4
	 *
	 * @param string $type → scripts|styles.
	 *
	 * @return array → url|path to minimized file
	 */
	protected static function get_routes_to_folder( $type ) {

		$url = isset( self::$unify[ "{$type}s" ] ) ? self::$unify[ "{$type}s" ] : self::$unify;

		return [
			'url'  => $url,
			'path' => self::get_path_from_url( $url ),
		];
	}

	/**
	 * Obtain information from processed files.
	 *
	 * @since 1.0.4
	 *
	 * @return void
	 */
	protected static function get_processed_files() {

		self::$files = get_option( 'wp-register-files', [] );
	}

	/**
	 * Get path from url.
	 *
	 * @since 1.0.4
	 *
	 * @param string $url → file url.
	 *
	 * @return string → filepath
	 */
	protected static function get_path_from_url( $url ) {

		return $_SERVER['DOCUMENT_ROOT'] . parse_url( $url, PHP_URL_PATH );
	}

	/**
	 * Check if the file was modified.
	 *
	 * @since 1.0.4
	 *
	 * @param string $filepath → path of the file.
	 *
	 * @return boolean
	 */
	protected static function is_modified_file( $filepath ) {

		$actual = filemtime( $filepath );

		$last = isset( self::$files[ $filepath ] ) ? self::$files[ $filepath ] : 0;

		if ( $actual !== $last ) {

			self::$files[ $filepath ] = $actual;

			self::$changes = true;

			return self::$changes;
		}

		return false;
	}

	/**
	 * Check if it matches the file hash.
	 *
	 * @since 1.0.4
	 *
	 * @param string $url  → external url.
	 * @param string $path → internal file path.
	 *
	 * @return boolean
	 */
	protected static function is_modified_hash( $url, $path ) {

		if ( self::is_external_url( $url ) ) {
			if ( sha1_file( $url ) !== sha1_file( $path ) ) {
				self::$changes = true;
				return self::$changes;
			}
		}

		return false;
	}

	/**
	 * Check if it's an external file.
	 *
	 * @since 1.0.4
	 *
	 * @param string $url → file url.
	 *
	 * @return boolean
	 */
	protected static function is_external_url( $url ) {

		return ( strpos( $url, get_site_url() ) === false );
	}

	/**
	 * Unify files.
	 *
	 * @since 1.0.4
	 *
	 * @param array  $params → paths and urls of files to unify.
	 * @param string $data   → initial string.
	 *
	 * @return boolean true
	 */
	protected static function unify_files( $params, $data = '' ) {

		$type      = $params['type'];
		$routes    = $params['routes'];
		$extension = ( 'style' == $type ) ? '.css' : '.js';
		$hash      = sha1( implode( '', $params['files'] ) );
		$min_file  = $routes['path'] . $hash . $extension;

		if ( ! is_file( $min_file ) || self::$changes ) {
			foreach ( $params['paths'] as $id => $path ) {
				if ( isset( $params['urls'][ $id ] ) ) {
					$url   = $params['urls'][ $id ];
					$path  = $routes['path'] . $params['files'][ $id ];
					$data .= self::save_external_file( $url, $path );
				}

				$data .= file_get_contents( $path );
			}

			$data = ( self::$minify ) ? self::compress_files( $data ) : $data;

			self::save_file( $min_file, $data );
		}

		self::set_processed_files();

		return self::set_new_params( $type, $hash, $routes['url'], $extension );
	}

	/**
	 * Save external file.
	 *
	 * @since 1.0.4
	 *
	 * @param string $url  → external url.
	 * @param string $path → internal file path.
	 *
	 * @return string → file content or empty
	 */
	protected static function save_external_file( $url, $path ) {

		$data = file_get_contents( $url );

		return ( $data && self::save_file( $path, $data ) ) ? $data : '';
	}

	/**
	 * File minifier.
	 *
	 * @since 1.0.4
	 *
	 * @author powerbuoy (https://github.com/powerbuoy)
	 *
	 * @param string $content → file content.
	 *
	 * @return array → unified parameters
	 */
	protected static function compress_files( $content ) {

		$var = array( "\r\n", "\r", "\n", "\t", '  ', '    ', '    ' );

		$content = preg_replace( '!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $content );

		$content = str_replace( $var, '', $content );
		$content = str_replace( '{ ', '{', $content );
		$content = str_replace( ' }', '}', $content );
		$content = str_replace( '; ', ';', $content );

		return $content;
	}

	/**
	 * Save file.
	 *
	 * @since 1.0.4
	 *
	 * @param string $path → internal file path.
	 * @param string $data → file content.
	 *
	 * @return boolean
	 */
	protected static function save_file( $path, $data ) {

		self::create_directory_from_file( $path );

		return file_put_contents( $path, $data );
	}

	/**
	 * Create directory where external/unified/minimized files will be saved.
	 *
	 * @since 1.0.4
	 *
	 * @param string $file → filepath.
	 *
	 * @return boolean
	 */
	protected static function create_directory_from_file( $file ) {

		$path = dirname( $file );

		if ( ! is_dir( $path ) ) {
			return mkdir( $path, 0777, true );
		}

		return true;
	}

	/**
	 * Set information from processed files.
	 *
	 * @since 1.0.4
	 *
	 * @return void
	 */
	protected static function set_processed_files() {

		if ( self::$changes ) {
			update_option( 'wp-register-files', self::$files );
		}
	}

	/**
	 * Set new parameters for the unified file.
	 *
	 * @since 1.0.4
	 *
	 * @param string $type      → script|style.
	 * @param string $hash      → filename hash.
	 * @param string $url       → path url.
	 * @param string $extension → file extension.
	 *
	 * @return boolean true
	 */
	protected static function set_new_params( $type, $hash, $url, $extension ) {

		$data = [

			'name'    => self::$id,
			'url'     => $url . $hash . $extension,
			'deps'    => self::unify_params( $type, 'deps' ),
			'version' => self::unify_params( $type, 'version', '1.0.0' ),
		];

		switch ( $type ) {
			case 'style':
				$data['media'] = self::unify_params( $type, 'media', 'all' );
				break;

			case 'script':
				$data['params'] = self::unify_params( $type, 'params' );
				$data['footer'] = self::unify_params( $type, 'footer', false );

				$data['params']['nonce'] = wp_create_nonce( self::$id );
				break;

			default:
		}

		self::$data[ $type ] = [ $data['name'] => $data ];

		return true;
	}

	/**
	 * Obtain all the parameters of a particular field and unify them.
	 *
	 * @since 1.0.4
	 *
	 * @param string $type    → script|style.
	 * @param string $field   → field to unify.
	 * @param string $default → default param.
	 *
	 * @return array → unified parameters
	 */
	protected static function unify_params( $type, $field, $default = '' ) {

		$data = array_column( self::$data[ $type ], $field );

		switch ( $field ) {
			case 'media':
			case 'footer':
			case 'version':
				foreach ( $data as $key => $value ) {
					if ( $data[0] !== $value ) {
						return $default;
					}
				}

				return ( isset( $data[0] ) && $data[0] ) ? $data[0] : $default;

			default:
				$params = [];

				foreach ( $data as $key => $value ) {
					$params = array_merge( $params, $value );
				}

				return array_unique( $params );
		}
	}
}


<?php
/* (c) Anton Medvedev <anton@medv.io>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Deployer\Console;

use Deployer\Initializer\Initializer;
use Deployer\Initializer\Template\CommonTemplate;
use Deployer\Initializer\Template\LaravelTemplate;
use Deployer\Initializer\Template\SymfonyTemplate;
use Deployer\Initializer\Template\Yii2AdvancedAppTemplate;
use Deployer\Initializer\Template\Yii2BasicAppTemplate;
use Deployer\Initializer\Template\YiiTemplate;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\Process;

/**
 * The command for initialize Deployer system in your project
 *
 * @author Vitaliy Zhuk <zhuk2205@gmail.com>
 */
class OriginalInitCommand extends Command
{
    /**
     * @var Initializer
     */
    private $initializer;

    /**
     * @var array
     */
    private $availableTemplates;

    /**
     * Construct
     *
     * @param string $name
     */
    public function __construct($name = null)
    {
        $this->initializer = $this->createInitializer();
        $this->availableTemplates = $this->initializer->getTemplateNames();

        parent::__construct($name);
    }

    /**
     * {@inheritDoc}
     */
    protected function configure()
    {
        $this
            ->setName('init')
            ->setDescription('Initialize deployer system in your project')
            ->addOption('template', 't', InputOption::VALUE_OPTIONAL, 'The template of you project. Available templates: ' . implode(', ', $this->availableTemplates))
            ->addOption('directory', null, InputOption::VALUE_OPTIONAL, 'The directory for create "deploy.php" file', getcwd())
            ->addOption('filename', null, InputOption::VALUE_OPTIONAL, 'The file name. Default "deploy.php"', 'deploy.php');
    }

    /**
     * {@inheritDoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $template = $input->getOption('template');
        $directory = $input->getOption('directory');
        $file = $input->getOption('filename');
        $params = [];

        if ($template === null) {
            $io = new SymfonyStyle($input, $output);
            $helper = $this->getHelper('question');
            $formatter = $this->getHelper('formatter');

            // Welcome message
            $output->writeln([
                '',
                $formatter->formatBlock('Welcome to the Deployer config generator', 'bg=blue;fg=white', true),
                '',
            ]);

            $io->text([
                'This utility will walk you through creating a deploy.php file.',
                'It only covers the most common items, and tries to guess sensible defaults.',
                '',
                'Press ^C at any time to quit.',
            ]);

            // Project type
            $template = $io->choice('Please select your project type', $this->availableTemplates, 'Common');

            // Repo
            $default = false;
            try {
                $default = (new Process('git remote get-url origin'))
                    ->mustRun()
                    ->getOutput();
                $default = trim($default);
            } catch (RuntimeException $e) {
                // pass
            }
            $params['repository'] = $io->ask('Repository', $default);

            // Privacy
            $io->text([
                'Contribute to the Deployer Development',
                '',
                'In order to help development and improve Deployer features in,',
                'Deployer has a setting for collection of usage data. This function',
                'collects anonymous usage data and sends it to Deployer. The data is',
                'used in Deployer development to get reliable statistics on which',
                'features are used (or not used). The information is not traceable',
                'to any individual or organization. Participation is voluntary,',
                'and you can change your mind at any time.',
                '',
                'Anonymous usage data contains Deployer version, php version, os type,',
                'name of the command being executed and whether it was successful or not,',
                'exception class name, count of hosts and anonymized project hash.',
                '',
                'If you would like to allow us to gather this information and help',
                'us develop a better tool, please add the code below.',
                '',
                "    <fg=white>set(<fg=cyan>'allow_anonymous_stats'</fg=cyan>, <fg=magenta;options=bold>true</fg=magenta;options=bold>);</fg=white>",
                '',
                'This function will not affect the performance of Deployer as',
                'the data is insignificant and transmitted in separate process.',
            ]);

            $params['allow_anonymous_stats'] = $GLOBALS['allow_anonymous_stats'] = $io->confirm('Do you confirm?');
        }

        $filePath = $this->initializer->initialize($template, $directory, $file, $params);

        $output->writeln(sprintf(
            '<info>Successfully created:</info> <comment>%s</comment>',
            $filePath
        ));
    }

    /**
     * Create a initializer system
     *
     * @return Initializer
     */
    protected function createInitializer()
    {
        $initializer = new Initializer();

        $initializer->addTemplate('Common', new CommonTemplate());
        $initializer->addTemplate('Laravel', new LaravelTemplate());
        $initializer->addTemplate('Symfony', new SymfonyTemplate());
        $initializer->addTemplate('Yii', new YiiTemplate());
        $initializer->addTemplate('Yii2 Basic App', new Yii2BasicAppTemplate());
        $initializer->addTemplate('Yii2 Advanced App', new Yii2AdvancedAppTemplate());

        return $initializer;
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Adds services tagged as sql relation to descriptors collection.
 */
class ExtractionDescriptorPass extends AbstractExtractionDescriptorPass implements CompilerPassInterface
{
    /**
     * {@inheritdoc}
     */
    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition('ongr_connections.sync.extraction_collection')) {
            return;
        }
        $collectionDefinition = $container->getDefinition('ongr_connections.sync.extraction_collection');
        foreach ($container->findTaggedServiceIds('ongr_connections.extraction_descriptor') as $id => $tags) {
            $definition = $container->getDefinition($id);
            $definition->addMethodCall('setName', [$id]);
            $this->addParameters($container, $definition);
            $collectionDefinition->addMethodCall('addDescriptor', [new Reference($id)]);
        }
    }
}


<?php namespace AdammBalogh\KeyValueStore\Adapter\FileAdapter;

use AdammBalogh\KeyValueStore\Adapter\Util;
use AdammBalogh\KeyValueStore\Exception\KeyNotFoundException;

/**
 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
 */
trait ValueTrait
{
    use ClientTrait;

    /**
     * Gets the value of a key.
     *
     * @param string $key
     *
     * @return mixed The value of the key.
     *
     * @throws KeyNotFoundException
     * @throws \Exception
     */
    public function get($key)
    {
        $getResult = $this->getValue($key);
        $unserialized = @unserialize($getResult);

        if (Util::hasInternalExpireTime($unserialized)) {

            $this->handleTtl($key, $unserialized['ts'], $unserialized['s']);

            $getResult = $unserialized['v'];
        }

        return $getResult;
    }

    /**
     * Sets the value of a key.
     *
     * @param string $key
     * @param mixed $value Can be any of serializable data type.
     *
     * @return bool True if the set was successful, false if it was unsuccessful.
     *
     * @throws \Exception
     */
    public function set($key, $value)
    {
        return $this->getClient()->set($key, $value);
    }

    /**
     * @param string $key
     *
     * @return string
     * @throws KeyNotFoundException
     * @throws \Exception
     */
    protected function getValue($key)
    {
        $getResult = $this->getClient()->get($key);
        if ($getResult === false) {
            throw new KeyNotFoundException($key);
        }

        return $getResult;
    }

    /**
     * If ttl is lesser or equals 0 delete key.
     *
     * @param string $key
     * @param int $expireSetTs
     * @param int $expireSec
     *
     * @return int ttl
     *
     * @throws KeyNotFoundException
     */
    protected function handleTtl($key, $expireSetTs, $expireSec)
    {
        $ttl = $expireSetTs + $expireSec - time();
        if ($ttl <= 0) {
            $this->getClient()->delete($key);
            throw new KeyNotFoundException();
        }

        return $ttl;
    }
}


<?php

namespace App\Classes;

use function Scaleplan\Helpers\get_env;

/**
 * Хэлпер для перевода текста посредством Yandex.Переводчика
 *
 * Class TranslateHelper
 *
 * @package App\Classes
 */
class TranslateHelper
{
    /**
     * Ключ к API Yandex Translate
     */
    public const YANDEX_TRANSLATE_API_KEY = ''; //trnsl.1.1.201...

    /**
     * Ссылка на Yandex.Переводчик для перевода
     */
    public const TRANSLATE_URL = 'https://translate.yandex.net/api/v1.5/tr.json/translate';

    /**
     * Ссылка на Yandex.Переводчик для отределения языка текста
     */
    public const LANG_DETECT_URL = 'https://translate.yandex.net/api/v1.5/tr.json/detect';

    /**
     * @return string
     */
    public static function getApiKey() : string
    {
        return get_env('YANDEX_TRANSLATE_API_KEY') ?? static::YANDEX_TRANSLATE_API_KEY;
    }

    /**
     * Вернуть язык сообщения
     *
     * @param string $message
     *
     * @return string
     */
    public static function getMessageLang(string $message): string
    {
        $currentLang = file_get_contents(
            static::LANG_DETECT_URL . '
            ?key=' . static::getApiKey() . '
            &text=' . $message . '
            &hint=en,ru'
        );

        return json_decode($currentLang, true)['lang'] ?? $message;
    }

    /**
     * Перевести сообщение на выбранный язык
     *
     * @param string $message - сообщение
     * @param string $toLang - на какой язык переводить
     * @param string|NULL $fromLang - с какого языка переводить
     *
     * @return string
     */
    public static function translate(string $message, string $toLang, string $fromLang = 'ru'): string
    {
        if ($fromLang === $toLang) {
            return $message;
        }

        if ($fromLang) {
            $requestLang = "$fromLang-$toLang";
        } else {
            $requestLang = $toLang;
        }

        $params = [
            'key' => static::getApiKey(),
            'text' => $message,
            'lang' => $requestLang,
            'format' => 'html'
        ];

        $translatedMessage = file_get_contents(static::TRANSLATE_URL . '?' . http_build_query($params));

        return json_decode($translatedMessage, true)['text'][0] ?? $message;
    }
}


<?php
/**
 * Arbit Xml document base
 *
 * This file is part of Arbit.
 *
 * Arbit is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; version 3 of the License.
 *
 * Arbit is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Arbit; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @package XML
 * @version $Revision$
 * @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
 */

namespace Arbit\Xml;

/*
 * XML reader
 *
 * Custom XML reader using the PHP xmlreader, exposing an interface similar to
 * simple XML, but implementing Cacheable, so the parsed XML structure can
 * be cached.
 */
class Document extends Node
{
    /**
     * Create XML document from file
     *
     * @param string $xmlFile
     * @return Document
     */
    public static function loadFile( $xmlFile )
    {
        // Check if user exists at all
        if ( !is_file( $xmlFile ) ||
             !is_readable( $xmlFile ) )
        {
            throw new NoSuchFileException( $xmlFile );
        }

        return self::parseXml( $xmlFile );
    }

    /**
     * Create XML document from string
     *
     * @param string $xmlString
     * @return Document
     */
    public static function loadString( $xmlString )
    {
        $xmlFile = tempnam( self::getSysTempDir(), 'xml_' );
        file_put_contents( $xmlFile, $xmlString );
        $xml = self::parseXml( $xmlFile );
        unlink( $xmlFile );
        return $xml;
    }

    /**
     * Returns the system temp directory.
     *
     * @return string
     */
    protected static function getSysTempDir()
    {
        if ( function_exists( 'sys_get_temp_dir' ) )
        {
            return sys_get_temp_dir();
        }
        else if ( $tmp = getenv( 'TMP' ) )
        {
            return $tmp;
        }
        else if ( $tmp = getenv( 'TEMP' ) )
        {
            return $tmp;
        }
        else if ( $tmp = getenv( 'TMPDIR' ) )
        {
            return $tmp;
        }
        return '/tmp';
    }

    /**
     * Parse XML file
     *
     * Parse the given XML into Node objects using the \XMLReader class.
     *
     * @param string $xmlFile
     * @return Node
     */
    protected static function parseXml( $xmlFile )
    {
        $reader = new \XMLReader();

        // Use custom error handling to suppress warnings and errors during
        // parsing.
        $libXmlErrors = libxml_use_internal_errors( true );

        // Try to open configuration file, and throw parsing exception if
        // something fails.
        $errors = array();

        // Just open, errors will not occure before actually reading.
        if ( !$reader->open( $xmlFile ) )
        {
            throw new XmlParserException( $xmlFile, array() );
        }

        // Current node, processed. Start with a reference to th root node.
        $current = $root = new Document();

        // Stack of parents for the current node. We store this list, because
        // we do not want to store a parent node reference in the nodes, as
        // this breaks with var_export'ing those structures.
        $parents = array( $root );

        // Start processing the XML document
        //
        // The read method may issue warning, even if
        // libxml_use_internal_errors was set to true. That sucks, and we need
        // to use the @ here...
        while( @$reader->read() )
        {
            switch( $reader->nodeType )
            {
                case \XMLReader::ELEMENT:
                    // A new element, which results in a new configuration node as
                    // a child of the current node
                    //
                    // Get name of new element
                    $nodeName = $reader->name;

                    // We create a new object, so append the current node as
                    // future parent node to the parent stack.
                    array_push( $parents, $current );

                    // Create new child and reference node as current working
                    // node
                    $current = $current->$nodeName = new Node();

                    // After reading the elements we need to know about this
                    // for further progressing
                    $emptyElement = $reader->isEmptyElement;

                    // Process elements attributes, if available
                    if ( $reader->hasAttributes )
                    {
                        // Read all attributes and store their values in the
                        // current configuration node
                        while( $reader->moveToNextAttribute() )
                        {
                            $current[$reader->name] = $reader->value;
                        }
                    }

                    if ( !$emptyElement )
                    {
                        // We only break for non empty elements.
                        //
                        // For empty elements the element may also be counted
                        // as a closing tag, so that we want also process the
                        // next case statement.
                        break;
                    }

                case \XMLReader::END_ELEMENT:
                    // At the end of a element set the current pointer back to its
                    // parent
                    //
                    // Pop new current node from parents stack
                    $current = array_pop( $parents );
                    break;

                case \XMLReader::TEXT:
                case \XMLReader::CDATA:
                    // Text and CData node are added as node content.
                    //
                    // Append string, in case several text or Cdata nodes exist
                    // in one node
                    $current->setContent( (string) $current . $reader->value );
                    break;

                // Everything else can be ignored for now..
            }
        }

        // Check if errors occured while reading configuration
        if ( count( $errors = libxml_get_errors() ) )
        {
            // Reset libxml error handling to old state
            libxml_use_internal_errors( $libXmlErrors );
            libxml_clear_errors();

            throw new XmlParserException( $xmlFile, $errors );
        }

        // Reset libxml error handling to old state
        libxml_use_internal_errors( $libXmlErrors );
        return $root->skipRoot();
    }

    /**
     * Skip root node
     *
     * SimpleXML offers direct access to the childs of the root, without any
     * information about the actual root node. We do the same by just skipping
     * from the root node its first child.
     *
     * @return void
     */
    protected function skipRoot()
    {
        $rootList = reset( $this->childs );
        return $rootList[0]->toDocument();
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 */

namespace ONGR\ConnectionsBundle\Crawler\Event;

use ONGR\ConnectionsBundle\EventListener\AbstractCrawlerSource;
use ONGR\ConnectionsBundle\Pipeline\Event\SourcePipelineEvent;
use ONGR\ElasticsearchBundle\DSL\Query\MatchAllQuery;
use ONGR\ElasticsearchBundle\ORM\Manager;
use ONGR\ElasticsearchBundle\ORM\Repository;

/**
 * Provides data from Elasticsearch repository.
 */
class RepositoryCrawlerSource extends AbstractCrawlerSource
{
    /**
     * @var Repository Elasticsearch repository.
     */
    protected $repository;

    /**
     * @var string
     */
    protected $scrollSetting = '1m';

    /**
     * Constructor.
     *
     * @param Manager $manager
     * @param string  $repositoryName
     */
    public function __construct(Manager $manager, $repositoryName)
    {
        $this->repository = $manager->getRepository($repositoryName);
    }

    /**
     * Source provider event.
     *
     * @param SourcePipelineEvent $sourceEvent
     */
    public function onSource(SourcePipelineEvent $sourceEvent)
    {
        $sourceEvent->addSource($this->getAllDocuments());
    }

    /**
     * Gets all documents by given type.
     *
     * @return DocumentIterator
     */
    public function getAllDocuments()
    {
        $matchAllQuery = new MatchAllQuery();
        $search = $this->repository
            ->createSearch()
            ->addQuery($matchAllQuery)
            ->setScroll($this->getScrollSetting())
            ->setSearchType('scan');
        $documents = $this->repository->execute($search);

        return $documents;
    }

    /**
     * @return string
     */
    private function getScrollSetting()
    {
        return $this->scrollSetting;
    }

    /**
     * @param string $scrollSetting
     */
    public function setScrollSetting($scrollSetting)
    {
        $this->scrollSetting = $scrollSetting;
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\Sync\SyncStorage;

use DateTime;
use InvalidArgumentException;
use ONGR\ConnectionsBundle\Sync\ActionTypes;
use ONGR\ConnectionsBundle\Sync\StorageManager\StorageManagerInterface;

/**
 * Class for SyncStorage storage manipulation.
 */
class SyncStorage implements SyncStorageInterface
{
    /**
     * @var StorageManagerInterface
     */
    private $storageManager;

    /**
     * Dependency injection.
     *
     * @param StorageManagerInterface $storageManager
     */
    public function __construct(StorageManagerInterface $storageManager)
    {
        $this->storageManager = $storageManager;
    }

    /**
     * {@inheritdoc}
     */
    public function save($operationType, $documentType, $documentId, DateTime $dateTime, array $shopIds = null)
    {
        if (!$this->isValidOperation($operationType) || empty($documentType) || $documentId === 0) {
            throw new InvalidArgumentException('Invalid parameters specified.');
        }

        $this->storageManager->addRecord($operationType, $documentType, $documentId, $dateTime, $shopIds);
    }

    /**
     * {@inheritdoc}
     */
    public function getChunk($size = 1, $documentType = null, $shopId = null)
    {
        if ($size === 0) {
            return null;
        }

        return $this->storageManager->getNextRecords($size, $documentType, $shopId);
    }

    /**
     * {@inheritdoc}
     */
    public function deleteItem($itemId, array $shopIds = null)
    {
        if ($itemId === 0) {
            return;
        }

        $this->storageManager->removeRecord($itemId, $shopIds);
    }

    /**
     * Validates operation type.
     *
     * @param string $operationType
     *
     * @return bool
     */
    private function isValidOperation($operationType)
    {
        $operationType = strtoupper($operationType);

        return in_array(
            $operationType,
            [
                ActionTypes::CREATE,
                ActionTypes::UPDATE,
                ActionTypes::DELETE,
            ]
        );
    }
}


<?php declare(strict_types = 1);

namespace Tlapnet\Chart;

use LogicException;
use Tlapnet\Chart\Segment\CategorySegment;
use Tlapnet\Chart\Serie\CategorySerie;

class CategoryChart extends AbstractChart
{

	/** @var Category[] */
	private $categories;

	/** @var string[] */
	private $categoryKeys;

	/** @var CategorySerie[] */
	private $series = [];

	/** @var mixed[] serie_index => group */
	private $groups = [];

	/**
	 * @param Category[] $categories
	 */
	public function __construct(array $categories)
	{
		$this->categories = $categories;
		$this->categoryKeys = [];

		foreach ($this->categories as $category) {
			$this->categoryKeys[] = $category->getKey();
		}

		$this->assertCategoryKeyUniqueness();
	}

	private function assertCategoryKeyUniqueness(): void
	{
		$uniqueKeys = array_unique($this->categoryKeys);

		if (count($this->categoryKeys) !== count($uniqueKeys)) {
			throw new LogicException('Category keys have to be unique.');
		}
	}

	/**
	 * @param mixed $group Valid PHP array key. If NULL then serie is ungrouped
	 */
	public function addSerie(CategorySerie $serie, $group = null): void
	{
		$categorizedSegments = $this->getCategorizedSegments($serie);
		$normalizedSerie = new CategorySerie($serie->getType(), $serie->getTitle(), $serie->getColor());

		foreach ($this->categoryKeys as $key) {
			if (isset($categorizedSegments[$key])) {
				$normalizedSerie->addSegment($categorizedSegments[$key]);
			} else {
				$normalizedSerie->addSegment(new CategorySegment($key, 0));
			}
		}

		$this->series[] = $normalizedSerie;
		$this->groups[] = $group;
	}

	/**
	 * @return CategorySegment[]
	 * @throws LogicException When any serie segment has undefined category
	 */
	private function getCategorizedSegments(CategorySerie $serie): array
	{
		$categorizedSegments = [];

		foreach ($serie->getSegments() as $segment) {
			if (!in_array($segment->getKey(), $this->categoryKeys, true)) {
				throw new LogicException(sprintf(
					'Serie "%s" has segment with undefined category key "%s".',
					$serie->getTitle(),
					$segment->getKey()
				));
			}

			$categorizedSegments[$segment->getKey()] = $segment;
		}

		return $categorizedSegments;
	}

	/**
	 * {@inheritdoc}
	 */
	protected function getTemplateParameters(): array
	{
		$params = parent::getTemplateParameters();
		$params['categories'] = $this->categories;
		$params['series'] = $this->series;
		$params['groups'] = $this->groups;

		return $params;
	}

}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\EventListener;

use ONGR\ConnectionsBundle\Pipeline\Event\ItemPipelineEvent;
use ONGR\ConnectionsBundle\Pipeline\Item\AbstractImportItem;
use ONGR\ElasticsearchBundle\Document\DocumentInterface;

/**
 * Basic implementation of modifier. Only copies matching properties.
 */
class ModifyEventListener extends AbstractImportModifyEventListener
{
    /**
     * @var string[]
     */
    private $copySkipFields = [];

    /**
     * @return string[]
     */
    public function getCopySkipFields()
    {
        return $this->copySkipFields;
    }

    /**
     * @param string[] $copySkipFields
     *
     * @return $this
     */
    public function setCopySkipFields($copySkipFields)
    {
        $this->copySkipFields = $copySkipFields;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    protected function modify(AbstractImportItem $eventItem, ItemPipelineEvent $event)
    {
        $this->transform(
            $eventItem->getDocument(),
            $eventItem->getEntity()
        );
    }

    /**
     * Copies properties that have matching getter and setter.
     *
     * @param DocumentInterface $document
     * @param object            $entity
     * @param string[]          $skip
     */
    protected function transform(DocumentInterface $document, $entity, $skip = null)
    {
        $entityMethods = get_class_methods($entity);
        $documentMethods = get_class_methods($document);
        if ($skip === null) {
            $skip = $this->getCopySkipFields();
        }

        foreach ($entityMethods as $method) {
            if (strpos($method, 'get') !== 0) {
                continue;
            }
            $property = substr($method, 3);
            if (in_array(lcfirst($property), $skip)) {
                continue;
            }
            $setter = 'set' . $property;
            if (in_array($setter, $documentMethods)) {
                $document->{$setter}($entity->{$method}());
            }
        }
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\Sync\Extractor;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;

/**
 * Common actions required for all extractors.
 */
abstract class AbstractExtractor
{
    /**
     * @var ContainerInterface
     */
    private $container;

    /**
     * @param ContainerInterface $container
     */
    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    /**
     * Gets ids of shops from the configuration.
     *
     * @return array
     */
    protected function getShopIds()
    {
        $shopIds = [];

        try {
            $shops = $this->container->getParameter('ongr_connections.shops');
        } catch (InvalidArgumentException $e) {
            $shops = [];
        }

        foreach ($shops as $shop) {
            $shopIds[] = $shop['shop_id'];
        }

        return $shopIds;
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\EventListener;

use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Mapping\ClassMetadataInfo;

/**
 * Listens to mapping events and processes data replacements.
 */
class LoadClassMetadataListener
{
    /**
     * @var array
     */
    protected $replacements;

    /**
     * @param array $replacements
     */
    public function __construct(array $replacements = [])
    {
        $this->replacements = $replacements;
    }

    /**
     * Adds single replacement.
     *
     * @param string $search
     * @param string $replace
     */
    public function addReplacement($search, $replace)
    {
        $this->replacements[$search] = $replace;
    }

    /**
     * Processes doctrine mapping metadata and does replacements, as provided in replacements map.
     *
     * @param LoadClassMetadataEventArgs $eventArgs
     */
    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
    {
        if (empty($this->replacements)) {
            return;
        }

        /** @var ClassMetadataInfo $metadata */
        $metadata = $eventArgs->getClassMetadata();

        // Handle table name.
        $tableName = $metadata->getTableName();
        if ($tableName) {
            $metadata->setPrimaryTable(
                [
                    'name' => $this->doReplacement($tableName),
                ]
            );
        }

        // Handle fields AKA columns.
        foreach ($metadata->getFieldNames() as $fieldName) {
            $mapping = $metadata->getFieldMapping($fieldName);
            foreach ($mapping as $key => $value) {
                if (is_string($value)) {
                    $mapping[$key] = $this->doReplacement($value);
                }
            }
            $metadata->setAttributeOverride($fieldName, $mapping);
        }

        // Handle associations AKA foreign keys.
        $associationMappings = $metadata->getAssociationMappings();
        foreach ($metadata->getAssociationNames() as $fieldName) {
            if (isset($associationMappings[$fieldName])) {
                $associationMapping = $associationMappings[$fieldName];
                if (isset($associationMapping['joinColumns'])) {
                    foreach ($associationMapping['joinColumns'] as $key => $joinColumn) {
                        $associationMapping['joinColumns'][$key]['name'] = $this->doReplacement($joinColumn['name']);
                        $associationMapping['joinColumns'][$key]['referencedColumnName'] = $this->doReplacement(
                            $joinColumn['referencedColumnName']
                        );
                    }
                    $metadata->setAssociationOverride($fieldName, $associationMapping);
                }
            }
        }

        // Handle discriminator.
        if (count($metadata->discriminatorMap)) {
            $this->processDiscriminatorMap($metadata);
        }
    }

    /**
     * Replace discriminator array key if needed.
     *
     * @param ClassMetadataInfo $metadata
     */
    protected function processDiscriminatorMap(ClassMetadataInfo $metadata)
    {
        $newMap = [];

        foreach ($metadata->discriminatorMap as $mapId => $mappedEntityName) {
            $newKey = $this->doReplacement($mapId);
            $newMap[$newKey] = $mappedEntityName;
        }

        $metadata->discriminatorMap = $newMap;
    }

    /**
     * Applies replacements to provided string.
     *
     * @param string $inputString
     *
     * @return string
     */
    protected function doReplacement($inputString)
    {
        if (is_string($inputString)) {
            $inputString = str_replace(
                array_keys($this->replacements),
                array_values($this->replacements),
                $inputString
            );
        }

        return $inputString;
    }
}


<?php

/**
 * Unauthorized copying or dissemination of this file, via any medium is strictly prohibited
 * Proprietary and confidential
 *
 * @author    Jorge Torres <jorge.torres@wondeotec.com>
 * @copyright Copyright (C) Wondeotec SA - All Rights Reserved
 * @license   LICENSE.txt
 */

namespace EB\SDK\RecipientSubscribe;

use EB\SDK\Exception\DataValidationException;
use EB\SDK\Validators\DataValidator;

/**
 * Recipient
 */
class Recipient implements \JsonSerializable
{
    use DataValidator;

    const STATUS_SUBSCRIBED = 'subscribed';
    const STATUS_UNSUBSCRIBED = 'unsubscribed';

    /**
     * @var string A valid email address
     */
    protected $emailAddress;

    /**
     * @var string The email address hash
     */
    protected $hash;

    /**
     * @var string The email service provider (if your email is email@domain.com, then the provider is domain.com)
     */
    protected $provider;

    /**
     * @var string The recipient identification on your system
     */
    protected $externalId;

    /**
     * @var string The site URL that recipient was subscribed
     */
    protected $subscriptionSource;

    /**
     * @var string A valid IPV6 address
     */
    protected $ipAddress;

    /**
     * @var \DateTime The recipient subscription date
     */
    protected $subscriptionDate;

    /**
     * @var string The ISO 3166-2 format (e.g: PT, ES, etc)
     */
    protected $country;

    /**
     * @var string The contact gender (Allowed values: "M", "F", "NA")
     */
    protected $gender;

    /**
     * @var string The recipient first name
     */
    protected $firstName;

    /**
     * @var string The recipient last name
     */
    protected $lastName;

    /**
     * @var \DateTime The recipient birth date
     */
    protected $birthDate;

    /**
     * @var string The recipient address
     */
    protected $address;

    /**
     * @var string The recipient zip code
     */
    protected $zipCode;

    /**
     * @var string The first recipient phone
     */
    protected $phone1;

    /**
     * @var string The second recipient phone
     */
    protected $phone2;

    /**
     * @var string The recipient title in the format that you want (ex.: "Mr.", "Mrs.", "Miss", etc)
     */
    protected $title;

    /**
     * @var \DateTime The recipient last activity date
     */
    protected $lastActivityDate;

    /**
     * @var string The recipient subscription date
     */
    protected $subscriptionStatus;

    /**
     * @var string The recipient unsubscription IP address
     */
    protected $unsubscriptionIp;

    /**
     * @var \DateTime The recipient unsubscription date
     */
    protected $unsubscriptionDate;

    /**
     * @var bool The recipient complaint status
     */
    protected $complaint;

    /**
     * @var \DateTime The complaint date
     */
    protected $complaintDate;

    /**
     * @var bool The recipient bounce status
     */
    protected $bounce;

    /**
     * @var \DateTime The bounce date
     */
    protected $bounceDate;

    /**
     * @var string The recipient language (Allowed ISO 639x, ex.: en-US, fr-CA, etc)
     */
    protected $language;

    /**
     * @var array $categories
     */
    protected $categories;

    /**
     * @return string
     */
    public function getEmailAddress()
    {
        return $this->emailAddress;
    }

    /**
     * @param string $emailAddress
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setEmailAddress($emailAddress)
    {
        $this->emailAddress = $emailAddress;

        return $this;
    }

    /**
     * @return string
     */
    public function getHash()
    {
        return $this->hash;
    }

    /**
     * @param string $hash
     *
     * @return Recipient
     */
    public function setHash($hash)
    {
        $this->hash = $hash;

        return $this;
    }

    /**
     * @return string
     */
    public function getProvider()
    {
        return $this->provider;
    }

    /**
     * @param string $provider
     *
     * @return Recipient
     */
    public function setProvider($provider)
    {
        $this->provider = $provider;

        return $this;
    }

    /**
     * @return string
     */
    public function getExternalId()
    {
        return $this->externalId;
    }

    /**
     * @param string $externalId
     *
     * @return Recipient
     */
    public function setExternalId($externalId)
    {
        $this->externalId = $externalId;

        return $this;
    }

    /**
     * @return string
     */
    public function getSubscriptionSource()
    {
        return $this->subscriptionSource;
    }

    /**
     * @param string $subscriptionSource
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setSubscriptionSource($subscriptionSource)
    {
        $this->subscriptionSource = $subscriptionSource;

        return $this;
    }

    /**
     * @return string
     */
    public function getIpAddress()
    {
        return $this->ipAddress;
    }

    /**
     * @param string $ipAddress
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setIpAddress($ipAddress)
    {
        $this->ipAddress = $ipAddress;

        return $this;
    }

    /**
     * @return \DateTime
     */
    public function getSubscriptionDate()
    {
        return $this->subscriptionDate;
    }

    /**
     * @param \DateTime $subscriptionDate
     *
     * @return Recipient
     */
    public function setSubscriptionDate(\DateTime $subscriptionDate)
    {
        $this->subscriptionDate = $subscriptionDate;

        return $this;
    }

    /**
     * @return string
     */
    public function getCountry()
    {
        return $this->country;
    }

    /**
     * @param string $country
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setCountry($country)
    {
        // Making all uppercase
        $country = strtoupper($country);

        $this->country = $country;

        return $this;
    }

    /**
     * @return string
     */
    public function getGender()
    {
        return $this->gender;
    }

    /**
     * @param string $gender
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setGender($gender)
    {
        // Making all uppercase
        $gender = strtoupper($gender);

        $this->gender = $gender;

        return $this;
    }

    /**
     * @return string
     */
    public function getFirstName()
    {
        return $this->firstName;
    }

    /**
     * @param string $firstName
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setFirstName($firstName)
    {
        $this->firstName = $firstName;

        return $this;
    }

    /**
     * @return string
     */
    public function getLastName()
    {
        return $this->lastName;
    }

    /**
     * @param string $lastName
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setLastName($lastName)
    {
        $this->lastName = $lastName;

        return $this;
    }

    /**
     * @return \DateTime
     */
    public function getBirthDate()
    {
        return $this->birthDate;
    }

    /**
     * @param \DateTime $birthDate
     *
     * @return Recipient
     */
    public function setBirthDate(\DateTime $birthDate)
    {
        $this->birthDate = $birthDate;

        return $this;
    }

    /**
     * @return string
     */
    public function getAddress()
    {
        return $this->address;
    }

    /**
     * @param string $address
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setAddress($address)
    {
        $this->address = $address;

        return $this;
    }

    /**
     * @return string
     */
    public function getZipCode()
    {
        return $this->zipCode;
    }

    /**
     * @param string $zipCode
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setZipCode($zipCode)
    {
        $this->zipCode = $zipCode;

        return $this;
    }

    /**
     * @return string
     */
    public function getPhone1()
    {
        return $this->phone1;
    }

    /**
     * @param string $phone1
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setPhone1($phone1)
    {
        $this->phone1 = $phone1;

        return $this;
    }

    /**
     * @return string
     */
    public function getPhone2()
    {
        return $this->phone2;
    }

    /**
     * @param string $phone2
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setPhone2($phone2)
    {
        $this->phone2 = $phone2;

        return $this;
    }

    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * @param string $title
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    /**
     * @return \DateTime
     */
    public function getLastActivityDate()
    {
        return $this->lastActivityDate;
    }

    /**
     * @param \DateTime $lastActivityDate
     *
     * @return Recipient
     */
    public function setLastActivityDate(\DateTime $lastActivityDate)
    {
        $this->lastActivityDate = $lastActivityDate;

        return $this;
    }

    /**
     * @return string
     */
    public function getSubscriptionStatus()
    {
        return $this->subscriptionStatus;
    }

    /**
     * @param string $subscriptionStatus
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setSubscriptionStatus($subscriptionStatus)
    {
        // Making all lowercase
        $subscriptionStatus = strtolower($subscriptionStatus);

        $this->subscriptionStatus = $subscriptionStatus;

        return $this;
    }

    /**
     * Set this recipient as subscribed.
     * @param \DateTime $subscriptionDate The recipient subscripiton date
     * @param string    $subscriptionIp The recipient subscription IP
     *
     * @return Recipient
     */
    public function setAsSubscribed(\DateTime $subscriptionDate, $subscriptionIp = '127.0.0.1')
    {
        $this->subscriptionStatus = self::STATUS_SUBSCRIBED;
        $this->subscriptionDate   = $subscriptionDate;
        $this->ipAddress          = $subscriptionIp;

        return $this;
    }

    /**
     * Set this recipient as unsubscribed
     * @param \DateTime $unsubscriptionDate The recipient unsubscription date
     * @param string    $unsubscriptionIp The recipient unsubscription Ip
     *
     * @return $this
     */
    public function setAsUnsubscribed(\DateTime $unsubscriptionDate, $unsubscriptionIp = '127.0.0.1')
    {
        $this->subscriptionStatus = self::STATUS_UNSUBSCRIBED;
        $this->unsubscriptionDate = $unsubscriptionDate;
        $this->unsubscriptionIp   = $unsubscriptionIp;

        return $this;
    }

    /**
     * @return string
     */
    public function getUnsubscriptionIp()
    {
        return $this->unsubscriptionIp;
    }

    /**
     * @param string $unsubscriptionIp
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setUnsubscriptionIp($unsubscriptionIp)
    {
        $this->unsubscriptionIp = $unsubscriptionIp;

        return $this;
    }

    /**
     * @return \DateTime
     */
    public function getUnsubscriptionDate()
    {
        return $this->unsubscriptionDate;
    }

    /**
     * @param \DateTime $unsubscriptionDate
     *
     * @return Recipient
     */
    public function setUnsubscriptionDate(\DateTime $unsubscriptionDate)
    {
        $this->unsubscriptionDate = $unsubscriptionDate;

        return $this;
    }

    /**
     * @return boolean
     */
    public function isComplaint()
    {
        return $this->complaint;
    }

    /**
     * @param boolean $complaint
     *
     * @return Recipient
     */
    public function setComplaint($complaint)
    {
        $this->complaint = $complaint;

        return $this;
    }

    /**
     * @return \DateTime
     */
    public function getComplaintDate()
    {
        return $this->complaintDate;
    }

    /**
     * @param \DateTime $complaintDate
     *
     * @return Recipient
     */
    public function setComplaintDate(\DateTime $complaintDate)
    {
        $this->complaintDate = $complaintDate;
        $this->complaint     = true;

        return $this;
    }

    /**
     * Set recipient as complaint
     * @param \DateTime $complaintDate The recipient complaint date
     *
     * @return Recipient
     */
    public function setAsComplaint(\DateTime $complaintDate)
    {
        $this->complaint     = true;
        $this->complaintDate = $complaintDate;

        return $this;
    }

        /**
     * @return boolean
     */
    public function isBounce()
    {
        return $this->bounce;
    }

    /**
     * @param boolean $bounce
     *
     * @return Recipient
     */
    public function setBounce($bounce)
    {
        $this->bounce = $bounce;

        return $this;
    }

    /**
     * @return \DateTime
     */
    public function getBounceDate()
    {
        return $this->bounceDate;
    }

    /**
     * @param \DateTime $bounceDate
     *
     * @return Recipient
     */
    public function setBounceDate(\DateTime $bounceDate)
    {
        $this->bounceDate = $bounceDate;
        $this->bounce     = true;

        return $this;
    }

    /**
     * Set recipient as bounce.
     * @param \DateTime $bounceDate The recipient bounce date
     *
     * @return Recipient
     */
    public function setAsBounce(\DateTime $bounceDate)
    {
        $this->bounce     = true;
        $this->bounceDate = $bounceDate;

        return $this;
    }

    /**
     * @return string
     */
    public function getLanguage()
    {
        return $this->language;
    }

    /**
     * @param string $language
     *
     * @return Recipient
     *
     * @throws \Exception
     */
    public function setLanguage($language)
    {
        $this->language = $language;

        return $this;
    }

    /**
     * @return array
     */
    public function getCategories()
    {
        return $this->categories;
    }

    /**
     * @param array $categories
     */
    public function setCategories($categories)
    {
        $this->categories = $categories;
    }

    /**
     * Validates the recipient data
     *
     * @return bool Returns true if the recipient's data was valid, false otherwise
     *
     * @throws \Exception
     */
    public function hasValidData()
    {
        // Country is mandatory
        if (! $this->isCountryValid($this->country)) {
            DataValidationException::throwInvalidCountryException();
        }

        // Email address or hash are mandatory
        if (empty($this->emailAddress) && empty($this->hash)) {
            DataValidationException::throwEmailAddressOrHashMandatoryException();
        }

        // If hash is defined, then the provider must be defined either
        if (! empty($this->hash) && empty($this->provider)) {
            DataValidationException::throwProviderMandatoryException();
        }

        // If email exists then it must be valid
        if (! empty($this->emailAddress) && ! $this->isEmailValid($this->emailAddress)) {
            DataValidationException::throwInvalidEmailAddressException($this->emailAddress);
        }

        // If subscription source exists then it must be valid
        if (! empty($this->subscriptionSource) && ! $this->isSubscriptionSourceValid($this->subscriptionSource)) {
            DataValidationException::throwInvalidSubscriptionSourceException();
        }

        // If IP address exists then it must be valid
        if (! empty($this->ipAddress) && ! $this->isIpAddressValid($this->ipAddress)) {
            DataValidationException::throwInvalidIpAddressException($this->ipAddress);
        }

        // If the gender exists then it must be valid
        if (! empty($this->gender) && ! $this->isGenderValid($this->gender)) {
            DataValidationException::throwInvalidGenderException($this->gender);
        }

        // If the first name exists then it must be valid
        if (! empty($this->firstName) && ! $this->isFirstNameValid($this->firstName)) {
            DataValidationException::throwInvalidFirstNameException($this->firstName);
        }

        // If the last name exists then it must be valid
        if (! empty($this->lastName) && ! $this->isLastNameValid($this->lastName)) {
            DataValidationException::throwInvalidLastNameException($this->lastName);
        }

        // If the address exists then it must be valid
        if (! empty($this->address) && ! $this->isAddressValid($this->address)) {
            DataValidationException::throwInvalidAddressException($this->address);
        }

        // If the zip code exists then it must be valid
        if (! empty($this->zipCode) && ! $this->isZipCodeValid($this->zipCode)) {
            DataValidationException::throwInvalidZipCodeException($this->zipCode);
        }

        // If the phone1 exists then it must be valid
        if (! empty($this->phone1) && ! $this->isPhoneValid($this->phone1)) {
            DataValidationException::throwInvalidPhone1Exception($this->phone1);
        }

        // If the phone2 exists then it must be valid
        if (! empty($this->phone2) && ! $this->isPhoneValid($this->phone2)) {
            DataValidationException::throwInvalidPhone2Exception($this->phone2);
        }

        // If the title exists then it must be valid
        if (! empty($this->title) && ! $this->isTitleValid($this->title)) {
            DataValidationException::throwInvalidTitleException($this->title);
        }

        if (! empty($this->subscriptionStatus) && ! $this->isSubscriptionStatusValid($this->subscriptionStatus)) {
            DataValidationException::throwInvalidSubscriptionStatusException($this->subscriptionStatus);
        }

        if (! empty($this->unsubscriptionIp) && ! $this->isIpAddressValid($this->unsubscriptionIp)) {
            DataValidationException::throwInvalidUnsubscriptionIpException($this->unsubscriptionIp);
        }

        if (! empty($this->language) && ! $this->isLanguageValid($this->language)) {
            DataValidationException::throwInvalidLanguageException($this->language);
        }

        if (! empty($this->categories) && ! $this->isCategoriesValid($this->categories)) {
            DataValidationException::throwInvalidCategoryException($this->categories);
        }

        if (json_encode($this) === false) {
            throw new \Exception(sprintf('The recipient\'s data can\'t be serialized: "%s"', json_last_error_msg()));
        }

        return true;
    }

    /**
     * (PHP 5 >= 5.4.0)

     * Specify data which should be serialized to JSON
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
     * @return mixed data which can be serialized by json_encode,
     * which is a value of any type other than a resource.
     * @throws \Exception
     */
    public function jsonSerialize()
    {
        $jsonRecipient = array();

        if ($this->getEmailAddress() != null) {
            $jsonRecipient['email_address'] = $this->getEmailAddress();
        }

        if ($this->getCountry() != null) {
            $jsonRecipient['country'] = $this->getCountry();
        }

        if ($this->getHash() != null) {
            $jsonRecipient['hash'] = $this->getHash();
        }

        if ($this->getProvider() != null) {
            $jsonRecipient['provider'] = $this->getProvider();
        }

        if ($this->getExternalId() != null) {
            $jsonRecipient['external_id'] = $this->getExternalId();
        }

        if ($this->getIpAddress() != null) {
            $jsonRecipient['ip_address'] = $this->getIpAddress();
        }

        if ($this->getSubscriptionSource() != null) {
            $jsonRecipient['subscription_source'] = $this->getSubscriptionSource();
        }

        if ($this->getSubscriptionDate() != null) {
            $jsonRecipient['subscription_date'] = $this->getSubscriptionDate()->format('Y-m-d h:i:s');
        }

        if ($this->getGender() != null) {
            $jsonRecipient['gender'] = $this->getGender();
        }

        if ($this->getFirstName() != null) {
            $jsonRecipient['first_name'] = $this->getFirstName();
        }

        if ($this->getLastName() != null) {
            $jsonRecipient['last_name'] = $this->getLastName();
        }

        if ($this->getBirthDate() != null) {
            $jsonRecipient['birthdate'] = $this->getBirthDate()->format('Y-m-d');
        }

        if ($this->getAddress() != null) {
            $jsonRecipient['address'] = $this->getAddress();
        }

        if ($this->getZipCode() != null) {
            $jsonRecipient['zipcode'] = $this->getZipCode();
        }

        if ($this->getPhone1() != null) {
            $jsonRecipient['phone1'] = $this->getPhone1();
        }

        if ($this->getPhone2() != null) {
            $jsonRecipient['phone2'] = $this->getPhone2();
        }

        if ($this->getTitle() != null) {
            $jsonRecipient['title'] = $this->getTitle();
        }

        if ($this->getLastActivityDate() != null) {
            $jsonRecipient['last_activity_date'] = $this->getLastActivityDate()->format('Y-m-d h:i:s');
        }

        if ($this->getSubscriptionStatus() != null) {
            $jsonRecipient['subscription_status'] = $this->getSubscriptionStatus();
        }

        if ($this->getUnsubscriptionIp() != null) {
            $jsonRecipient['unsubscription_ip'] = $this->getUnsubscriptionIp();
        }

        if ($this->getUnsubscriptionDate() != null) {
            $jsonRecipient['unsubscription_date'] = $this->getUnsubscriptionDate()->format('Y-m-d h:i:s');
        }

        if ($this->isComplaint() != null) {
            $jsonRecipient['complaint'] = $this->isComplaint();
        }

        if ($this->getComplaintDate() != null) {
            $jsonRecipient['complaint_date'] = $this->getComplaintDate()->format('Y-m-d h:i:s');
        }

        if ($this->isBounce() != null) {
            $jsonRecipient['bounce'] = $this->isBounce();
        }

        if ($this->getBounceDate() != null) {
            $jsonRecipient['bounce_date'] = $this->getBounceDate()->format('Y-m-d h:i:s');
        }

        if ($this->getLanguage() != null) {
            $jsonRecipient['language'] = $this->getLanguage();
        }

        if ($this->getCategories() != null) {
            $jsonRecipient['category_ids'] = $this->getCategories();
        }

        return $jsonRecipient;
    }
}


<?php

namespace Desarrolla2\Bundle\RSSClientBundle\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
 *
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
 */
class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritDoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $treeBuilder->root('rss_client')
            ->children()
                ->arrayNode('cache')
                    ->addDefaultsIfNotSet()
                    ->children()
                        ->scalarNode('adapter')
                            ->defaultValue('Desarrolla2\Cache\Adapter\NotCache')
                            ->cannotBeEmpty()
                        ->end()
                        ->scalarNode('ttl')
                            ->defaultValue('3600')
                            ->cannotBeEmpty()
                        ->end()
                    ->end()
                ->end()
                ->arrayNode('processors')
                    ->prototype('scalar')
                    ->end()
                ->end()
                ->arrayNode('channels')
                    ->useAttributeAsKey('id')
                    ->info('RSS Client Channels Configuration')
                    ->treatNullLike(array())
                        ->prototype('array')
                        ->treatNullLike(array())
                            ->prototype('scalar')
                            ->treatNullLike(null)
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
            ;
        return $treeBuilder;
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\Command;

use ONGR\ConnectionsBundle\Pipeline\PipelineStarter;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * AbstractStartServiceCommand -  starts service.
 */
abstract class AbstractStartServiceCommand extends ContainerAwareCommand
{
    /**
     * {@inheritdoc}
     */
    public function __construct($name, $description)
    {
        $this->setDescription($description);
        parent::__construct($name);
    }

    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->addStandardArgument();
    }

    /**
     * Starts service with provided parameters.
     *
     * @param InputInterface  $input
     * @param OutputInterface $output
     * @param string          $serviceClass
     * @param string          $prefix
     */
    protected function start(InputInterface $input, OutputInterface $output, $serviceClass, $prefix)
    {
        $benchmark = new CommandBenchmark($output);
        $benchmark->start();

        /** @var PipelineStarter $service */
        $service = $this->getContainer()->get($serviceClass);
        $factory = $service->getPipelineFactory();
        $factory->setProgressBar(new ProgressBar($output));
        $service->startPipeline($prefix, $input->getArgument('target'));

        $benchmark->finish();
    }

    /**
     * Adds argument with standard parameters.
     */
    protected function addStandardArgument()
    {
        $this->addArgument(
            'target',
            InputArgument::OPTIONAL,
            'Set a specific pipeline event name.'
        );
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\Pipeline;

use ONGR\ConnectionsBundle\Pipeline\Event\ItemPipelineEvent;

/**
 * Provides a static method for skipping unwanted items.
 */
class ItemSkipper
{
    /**
     * Skips item, marks event as skipped with a reason.
     *
     * @param ItemPipelineEvent $event
     * @param string            $reason
     */
    public static function skip(ItemPipelineEvent $event, $reason = '')
    {
        $itemSkip = new ItemSkip();
        $itemSkip->setReason($reason);
        $event->setItemSkip($itemSkip);
        $event->stopPropagation();
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\Sync;

use Doctrine\ORM\EntityManagerInterface;
use ONGR\ConnectionsBundle\Pipeline\Item\SyncExecuteItem;
use ONGR\ConnectionsBundle\Sync\SyncStorage\SyncStorage;
use ONGR\ElasticsearchBundle\ORM\Repository;

/**
 * This class is able to iterate over entities without storing objects in memory.
 */
class SyncStorageImportIterator implements \Iterator
{
    /**
     * @var SyncStorage
     */
    private $syncStorage;

    /**
     * @var Repository Elasticsearch repository.
     */
    private $repository;

    /**
     * @var mixed
     */
    private $currentEntity;

    /**
     * @var array|null
     */
    private $currentChunk;

    /**
     * @var int
     */
    private $shopId;

    /**
     * @var string
     */
    private $documentType;

    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    /**
     * @var string
     */
    private $entityClass;

    /**
     * @var bool
     */
    private $valid;

    /**
     * @param array                  $syncStorageParams
     * @param Repository             $repository
     * @param EntityManagerInterface $entityManager
     * @param string                 $entityClass
     */
    public function __construct(
        $syncStorageParams,
        Repository $repository,
        EntityManagerInterface $entityManager,
        $entityClass
    ) {
        $this->syncStorage = $syncStorageParams['sync_storage'];
        $this->shopId = $syncStorageParams['shop_id'];
        $this->documentType = $syncStorageParams['document_type'];
        $this->repository = $repository;
        $this->entityManager = $entityManager;
        $this->entityClass = $entityClass;
    }

    /**
     * This iterator cannot rewind. Method rewind() just initializes iterator before usage in foreach cycle.
     */
    public function rewind()
    {
        $this->next();
    }

    /**
     * {@inheritdoc}
     */
    public function current()
    {
        return new SyncExecuteItem(
            $this->currentEntity,
            $this->repository->createDocument(),
            $this->currentChunk[0]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function next()
    {
        $this->currentChunk = $this->syncStorage->getChunk(1, $this->documentType, $this->shopId);

        if (empty($this->currentChunk)) {
            $this->valid = false;

            return;
        }

        $this->currentEntity = $this
            ->entityManager
            ->getRepository($this->entityClass)->find($this->currentChunk[0]['document_id']);

        if (!empty($this->currentEntity) || $this->currentChunk[0]['type'] == ActionTypes::DELETE) {
            $this->valid = true;

            return;
        }

        $this->valid = false;
    }

    /**
     * {@inheritdoc}
     */
    public function key()
    {
        if ($this->valid()) {
            return $this->currentChunk[0]['document_id'];
        }

        return 0;
    }

    /**
     * {@inheritdoc}
     */
    public function valid()
    {
        return $this->valid;
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\Pipeline;

use ONGR\ConnectionsBundle\Pipeline\Event\ContextAwareTrait;
use ONGR\ConnectionsBundle\Pipeline\Event\FinishPipelineEvent;
use ONGR\ConnectionsBundle\Pipeline\Event\ItemPipelineEvent;
use ONGR\ConnectionsBundle\Pipeline\Event\SourcePipelineEvent;
use ONGR\ConnectionsBundle\Pipeline\Event\StartPipelineEvent;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Basic pipeline implementation using dispatcher.
 */
class Pipeline implements PipelineInterface
{
    use ContextAwareTrait;

    const EVENT_SUFFIX_SOURCE = 'source';
    const EVENT_SUFFIX_MODIFY = 'modify';
    const EVENT_SUFFIX_CONSUME = 'consume';
    const EVENT_SUFFIX_FINISH = 'finish';
    const EVENT_SUFFIX_START = 'start';

    /**
     * @var string Pipeline name used in event naming.
     */
    private $name;

    /**
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * @var ProgressBar
     */
    private $progressBar = null;

    /**
     * {@inheritdoc}
     */
    public function start()
    {
        $dispatcher = $this->getDispatcher();
        $sourceEvent = new SourcePipelineEvent();
        $sourceEvent->setContext($this->getContext());
        $dispatcher->dispatch(
            $this->getEventName(self::EVENT_SUFFIX_SOURCE),
            $sourceEvent
        );
        $this->setContext($sourceEvent->getContext());
        $sources = $sourceEvent->getSources();
        $outputs = [];

        $startEvent = new StartPipelineEvent();
        $startEvent->setContext($this->getContext());
        $startEvent->setItemCount($this->countSourceItems($sources));

        $dispatcher->dispatch(
            $this->getEventName(self::EVENT_SUFFIX_START),
            $startEvent
        );
        $this->setContext($startEvent->getContext());

        $count = $this->countSourceItems($sources);

        $this->progressBar && $this->progressBar->setRedrawFrequency($count > 10 ? $count / 10 : 1);
        $this->progressBar && $this->progressBar->start($count);

        foreach ($sources as $source) {
            foreach ($source as $item) {
                $itemEvent = new ItemPipelineEvent($item);
                $itemEvent->setContext($this->getContext());

                $dispatcher->dispatch(
                    $this->getEventName(self::EVENT_SUFFIX_MODIFY),
                    $itemEvent
                );

                $dispatcher->dispatch(
                    $this->getEventName(self::EVENT_SUFFIX_CONSUME),
                    $itemEvent
                );

                $output = $itemEvent->getOutput();
                if ($output !== null) {
                    $outputs[] = $output;
                }

                $this->setContext($itemEvent->getContext());

                $this->progressBar && $this->progressBar->advance();
            }
        }

        $finishEvent = new FinishPipelineEvent();
        $finishEvent->setContext($this->getContext());
        $dispatcher->dispatch(
            $this->getEventName(self::EVENT_SUFFIX_FINISH),
            $finishEvent
        );

        $this->progressBar && $this->progressBar->finish();

        return ['outputs' => $outputs];
    }

    /**
     * {@inheritdoc}
     */
    public static function generateEventName($pipelineName, $suffix)
    {
        return "ongr.pipeline.{$pipelineName}.{$suffix}";
    }

    /**
     * {@inheritdoc}
     */
    public function getEventName($suffix)
    {
        return static::generateEventName($this->getName(), $suffix);
    }

    /**
     * {@inheritdoc}
     *
     * @see Pipeline::$name
     */
    public function __construct($name)
    {
        $this->setName($name);
    }

    /**
     * {@inheritdoc}
     */
    public function getProgressBar()
    {
        return $this->progressBar;
    }

    /**
     * {@inheritdoc}
     */
    public function setProgressBar($progress)
    {
        $this->progressBar = $progress;
    }

    /**
     * {@inheritdoc}
     */
    public function getDispatcher()
    {
        return $this->dispatcher;
    }

    /**
     * {@inheritdoc}
     */
    public function setDispatcher($dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * {@inheritdoc}
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Counts number of items in the services.
     *
     * @param \Traversable[]|array[] $sources
     *
     * @return int
     */
    private function countSourceItems($sources)
    {
        $count = 0;
        foreach ($sources as $source) {
            $count += count($source);
        }

        return $count;
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\EventListener;

use ONGR\ConnectionsBundle\Pipeline\Event\ItemPipelineEvent;

/**
 * Provides functionality for skipping pipeline items.
 */
abstract class AbstractConsumeEventListener
{
    /**
     * Entry point of consume event.
     *
     * @param ItemPipelineEvent $event
     */
    public function onConsume(ItemPipelineEvent $event)
    {
        if ($event->getItemSkip()) {
            $this->skip($event);
        } else {
            $this->consume($event);
        }
    }

    /**
     * Called when item should be skipped.
     *
     * @param ItemPipelineEvent $event
     */
    public function skip(ItemPipelineEvent $event)
    {
    }

    /**
     * Called when item should be consumed.
     *
     * @param ItemPipelineEvent $event
     */
    abstract public function consume(ItemPipelineEvent $event);
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\Service;

use ONGR\ConnectionsBundle\Document\Pair;
use ONGR\ElasticsearchBundle\Document\DocumentInterface;
use ONGR\ElasticsearchBundle\ORM\Manager;
use ONGR\ElasticsearchBundle\ORM\Repository;

/**
 * Responsible for managing pairs actions.
 */
class PairStorage
{
    /**
     * @var Manager
     */
    protected $manager;

    /**
     * @var Repository
     */
    protected $repository;

    /**
     * @param Manager $manager
     */
    public function __construct(Manager $manager)
    {
        $this->manager = $manager;
        $this->repository = $this->manager->getRepository('ONGRConnectionsBundle:Pair');
    }

    /**
     * Returns pair value by key.
     *
     * @param string $key
     *
     * @return mixed
     */
    public function get($key)
    {
        $pair = $this->repository->find($key);

        return $pair ? $pair->getValue() : null;
    }

    /**
     * Sets pair value. Returns pair with values.
     *
     * @param string $key
     * @param mixed  $value
     *
     * @return DocumentInterface
     */
    public function set($key, $value)
    {
        $pair = $this->repository->find($key);
        if ($pair === null) {
            $pair = new Pair();
            $pair->setId($key);
        }

        $pair->setValue($value);
        $this->save($pair);

        return $pair;
    }

    /**
     * Removes pair by key.
     *
     * @param string $key
     */
    public function remove($key)
    {
        $pair = $this->repository->find($key);
        if ($pair !== null) {
            $this->repository->remove($pair->getId());
            $this->manager->flush();
            $this->manager->refresh();
        }
    }

    /**
     * Saves pair object.
     *
     * @param Pair $pair
     */
    private function save(Pair $pair)
    {
        $this->manager->persist($pair);
        $this->manager->commit();
        $this->manager->refresh();
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\UrlInvalidator;

use ONGR\ElasticsearchBundle\DSL\Filter\LimitFilter;
use ONGR\ElasticsearchBundle\DSL\Query\Query;
use ONGR\ElasticsearchBundle\DSL\Query\TermQuery;
use ONGR\ElasticsearchBundle\ORM\Manager;
use ONGR\RouterBundle\Document\SeoAwareInterface;
use ONGR\RouterBundle\Document\UrlObject;
use Symfony\Component\Process\Process;
use Symfony\Component\Routing\RouterInterface;

/**
 * Invalidates URLs.
 */
class UrlInvalidatorService
{
    /**
     * @var array URLs to invalidate.
     */
    protected $urlCache;

    /**
     * @var array Values and field of documents whose links needs to be invalidated.
     */
    protected $documentParamCache;

    /**
     * @var RouterInterface
     */
    protected $router;

    /**
     * @var Manager
     */
    protected $manager;

    /**
     * @var string Base project URL.
     */
    protected $baseUrl;

    /**
     * @var string|null Root dir path.
     */
    protected $rootDir;

    /**
     * @var string Cache script path from Root dir.
     */
    protected $cacheScript;

    /**
     * @var float CURL request timeout.
     */
    protected $curlTimeout;

    /**
     * @var DocumentUrlCollectorInterface[]
     */
    protected $urlCollectors;

    /**
     * @var bool If set to false, default seo urls will not be invalidated.
     */
    protected $invalidateSeoUrls;

    /**
     * @param RouterInterface $router
     * @param Manager         $manager
     * @param string          $baseUrl
     * @param float           $curlTimeout
     * @param string          $rootDir
     * @param string          $cacheScript
     * @param bool            $invalidateSeoUrls
     */
    public function __construct(
        RouterInterface $router,
        Manager $manager,
        $baseUrl,
        $curlTimeout,
        $rootDir,
        $cacheScript = 'bin/varnish',
        $invalidateSeoUrls = false
    ) {
        $this->router = $router;
        $this->manager = $manager;
        $this->urlCache = [];
        $this->documentParamCache = [];
        $this->baseUrl = $baseUrl;
        $this->rootDir = $rootDir;
        $this->curlTimeout = $curlTimeout;
        $this->urlCollectors = [];
        $this->cacheScript = $cacheScript;
        $this->invalidateSeoUrls = $invalidateSeoUrls;
    }

    /**
     * Adds URL which should be invalidated.
     *
     * @param string $url
     */
    public function addUrl($url)
    {
        $this->urlCache[$url] = true;
    }

    /**
     * Adds multiple urls.
     *
     * @param array $urls
     */
    public function addUrls($urls)
    {
        foreach ($urls as $url) {
            $this->addUrl($url);
        }
    }

    /**
     * Returns array of links.
     *
     * @return array
     */
    public function getUrls()
    {
        return array_keys($this->urlCache);
    }

    /**
     * Adds document whose links should be invalidated.
     *
     * @param string $field
     * @param string $value
     */
    public function addDocumentParameter($field, $value)
    {
        $this->documentParamCache[md5($value . $field)] = [$field, $value];
    }

    /**
     * Adds multiple documents whose links should be invalidated.
     *
     * @param array $params Add as array($field, $value).
     */
    public function addMultipleDocumentParameters(array $params)
    {
        foreach ($params as $param) {
            $this->addDocumentParameter($param[0], $param[1]);
        }
    }

    /**
     * Returns array of links got by document id.
     *
     * @return array
     */
    protected function getUrlsByDocumentParameter()
    {
        if (count($this->documentParamCache) < 1) {
            return [];
        }

        $urls = [];
        $query = new Query();
        $queryTerms = [];

        foreach ($this->documentParamCache as $param) {
            $queryTerms[$param[0]][] = $param[1];
        }

        foreach ($queryTerms as $field => $values) {
            $termQuery = new TermQuery($field, $values);
            $query->addQuery($termQuery, 'should');
        }

        $limitFilter = new LimitFilter(count($this->documentParamCache));
        $repository = $this->manager->getRepository('MultiModel');
        $search = $repository->createSearch()->addQuery($query)
            ->addFilter($limitFilter);
        $documents = $repository->execute($search);

        // Add all category urls to invalidate.
        foreach ($documents as $document) {
            if (is_array($document->url)) {
                foreach ($document->url as $url) {
                    $urls[] = $url['url'];
                }
            }
        }

        array_walk($urls, [$this, 'addWildcard']);
        $this->addUrls($urls);

        return $urls;
    }

    /**
     * Creates temporary file to store urls.
     *
     * @return string
     */
    public function createUrlsTempFile()
    {
        $hash = md5(microtime(true));
        $links = array_merge($this->getUrls(), $this->getUrlsByDocumentParameter());
        $urlsFile = "/tmp/urls_{$hash}.txt";
        $urls = [];

        foreach ($links as $url) {
            $separator = ($url[0] !== '/') ? '/' : '';
            $urls[] = $this->baseUrl . $separator . $url;
        }
        file_put_contents($urlsFile, implode(PHP_EOL, $urls));

        return $urlsFile;
    }

    /**
     * Invalidates collected URLs.
     *
     * @return string Executed file name.
     */
    public function invalidate()
    {
        $script = escapeshellcmd($this->rootDir . "/../{$this->cacheScript}");
        $urlsFile = escapeshellarg($this->createUrlsTempFile());
        $curlTimeout = escapeshellarg($this->curlTimeout);
        // Execute in background.
        $process = new Process(sprintf('%s %s %s', $script, $urlsFile, $curlTimeout));
        $process->start();

        $this->resetCache();

        return $urlsFile;
    }

    /**
     * Resets URLs cache.
     */
    protected function resetCache()
    {
        $this->urlCache = [];
        $this->documentParamCache = [];
    }

    /**
     * Collect all urls for invalidation.
     *
     * @param string            $type
     * @param SeoAwareInterface $document
     */
    public function loadUrlsFromDocument($type, SeoAwareInterface $document)
    {
        if ($this->invalidateSeoUrls) {
            // Default behavior.
            $urls = $document->getUrls();

            if (is_array($urls) || $urls instanceof \Traversable) {
                /** @var UrlObject $url */
                foreach ($urls as $url) {
                    $this->addUrl($url->getUrl());
                }
            }
        }
        // Special behavior from bundles.
        foreach ($this->urlCollectors as $collector) {
            $this->addUrls($collector->getDocumentUrls($type, $document, $this->router));
            $this->addMultipleDocumentParameters($collector->getDocumentParameters($type, $document));
        }
    }

    /**
     * Urls by type.
     *
     * @param string $type
     */
    public function loadUrlsByType($type)
    {
        foreach ($this->urlCollectors as $collector) {
            $this->addUrls($collector->getUrlsByType($type, $this->router));
        }
    }

    /**
     * Add '*' to end if string endings with '/'.
     *
     * @param string $item
     */
    protected function addWildcard(&$item)
    {
        if (substr($item, -1) === '/') {
            $item .= '*';
        }
    }

    /**
     * Adds additional URL collector service.
     *
     * @param DocumentUrlCollectorInterface $collector
     */
    public function addUrlCollector(DocumentUrlCollectorInterface $collector)
    {
        $this->urlCollectors[] = $collector;
    }

    /**
     * Setter for $invalidateSeoUrls.
     *
     * @param bool $invalidateSeoUrls
     */
    public function setInvalidateSeoUrls($invalidateSeoUrls)
    {
        $this->invalidateSeoUrls = $invalidateSeoUrls;
    }
}


<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ConnectionsBundle\Sync\DiffProvider\SyncJobs;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\Table;
use InvalidArgumentException;
use ONGR\ConnectionsBundle\Sync\SqlValidator;

/**
 * The service to create/update database table for synchronization jobs.
 */
class TableManager
{
    /**
     * @var Connection
     */
    private $connection;

    /**
     * @var string
     */
    private $tableName;

    /**
     * @var array
     */
    private $shops;

    /**
     * Constructor.
     *
     * @param Connection $connection
     * @param string     $tableName
     * @param array      $shops
     *
     * @throws \LogicException
     */
    public function __construct($connection, $tableName = 'ongr_sync_jobs', array $shops = [])
    {
        if ($connection === null) {
            throw new \LogicException('DBAL connection was not injected. Doctrine is missing?');
        }

        $this->connection = $connection;
        $this->setTableName($tableName);
        $this->shops = $shops;
    }

    /**
     * Creates table for sync jobs.
     *
     * @param Connection|null $connection
     *
     * @return bool|null
     */
    public function createTable($connection = null)
    {
        $connection = $connection ? : $this->connection;
        $schemaManager = $connection->getSchemaManager();

        if ($schemaManager->tablesExist([$this->tableName])) {
            return null;
        }

        $table = new Table($this->tableName);
        $this->buildTable($table);
        $schemaManager->createTable($table);

        return true;
    }

    /**
     * Updates table for sync jobs. Returns NULL if table is up-to-date.
     *
     * @param Connection|null $connection
     *
     * @return bool|null
     */
    public function updateTable($connection = null)
    {
        $connection = $connection ? : $this->connection;
        $schemaManager = $connection->getSchemaManager();

        if (!$schemaManager->tablesExist([$this->tableName])) {
            return false;
        }

        $table = new Table($this->tableName);
        $this->buildTable($table);
        $oldTable = $schemaManager->listTableDetails($this->tableName);

        $comparator = new Comparator();
        $diff = $comparator->diffTable($oldTable, $table);

        if (!$diff) {
            return null;
        }

        $schemaManager->alterTable($diff);

        return true;
    }

    /**
     * Returns connection.
     *
     * @return Connection
     */
    public function getConnection()
    {
        return $this->connection;
    }

    /**
     * Returns table name.
     *
     * @return string
     */
    public function getTableName()
    {
        return $this->tableName;
    }

    /**
     * Set table name.
     *
     * @param string $tableName
     */
    public function setTableName($tableName)
    {
        $this->tableName = SqlValidator::validateTableName($tableName);
    }

    /**
     * Builds table structure.
     *
     * @param Table $table
     */
    protected function buildTable(Table $table)
    {
        $table->addColumn('id', 'bigint')
            ->setUnsigned(true)
            ->setAutoincrement(true);

        $table->addColumn('type', 'string')
            ->setLength(1)
            ->setComment('C-CREATE(INSERT),U-UPDATE,D-DELETE');

        $table->addColumn('document_type', 'string')
            ->setLength(32);

        $table->addColumn('document_id', 'string')
            ->setLength(32);

        $table->addColumn('update_type', 'smallint')
            ->setDefault(1)
            ->setComment('0-partial,1-full');

        $table->addColumn('timestamp', 'datetime');

        $table->setPrimaryKey(['id']);

        $this->addStatusField($table);
    }

    /**
     * Dynamically add status field or fields in case of multi-shop.
     *
     * @param Table $table
     */
    protected function addStatusField(Table $table)
    {
        if (empty($this->shops)) {
            $table->addColumn('status', 'boolean', ['default' => 0])->setComment('0-new,1-done');
            $table->addIndex(['status']);
        } else {
            foreach ($this->shops as $shop) {
                $fieldName = "status_{$shop}";
                $table->addColumn($fieldName, 'boolean', ['default' => 0])->setComment('0-new,1-done');
                $table->addIndex([$fieldName]);
            }
        }
    }
}