master 3432bca679f9 cached
46 files
105.3 KB
26.1k tokens
199 symbols
1 requests
Download .txt
Repository: richsage/RMSPushNotificationsBundle
Branch: master
Commit: 3432bca679f9
Files: 46
Total size: 105.3 KB

Directory structure:
gitextract_uz_kv258/

├── .gitignore
├── .travis.yml
├── Command/
│   └── TestPushCommand.php
├── DependencyInjection/
│   ├── Compiler/
│   │   └── AddHandlerPass.php
│   ├── Configuration.php
│   └── RMSPushNotificationsExtension.php
├── Device/
│   ├── Types.php
│   └── iOS/
│       └── Feedback.php
├── Exception/
│   └── InvalidMessageTypeException.php
├── LICENSE
├── Message/
│   ├── AndroidMessage.php
│   ├── AppleMessage.php
│   ├── BlackberryMessage.php
│   ├── MacMessage.php
│   ├── MessageInterface.php
│   ├── WindowsphoneMessage.php
│   └── iOSMessage.php
├── README.md
├── RMSPushNotificationsBundle.php
├── Resources/
│   └── config/
│       ├── android.xml
│       ├── blackberry.xml
│       ├── ios.xml
│       ├── mac.xml
│       ├── services.xml
│       └── windowsphone.xml
├── Service/
│   ├── EventListener.php
│   ├── EventListenerInterface.php
│   ├── Notifications.php
│   ├── OS/
│   │   ├── AndroidGCMNotification.php
│   │   ├── AndroidNotification.php
│   │   ├── AppleNotification.php
│   │   ├── BlackberryNotification.php
│   │   ├── MicrosoftNotification.php
│   │   └── OSNotificationServiceInterface.php
│   └── iOSFeedback.php
├── Tests/
│   ├── DependencyInjection/
│   │   └── ConfigurationTest.php
│   ├── Message/
│   │   ├── AndroidMessageTest.php
│   │   ├── BlackberryMessageTest.php
│   │   ├── MacMessageTest.php
│   │   ├── WindowsphoneMessageTest.php
│   │   └── iOSMessageTest.php
│   ├── autoload.php.dist
│   └── bootstrap.php
├── composer.json
├── phpunit.travis.xml
└── phpunit.xml.dist

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
vendor

================================================
FILE: .travis.yml
================================================
language: php

before_install:
  # If PHP >= 5.6, download & install PHPunit 5.7 to avoid builds failures
  - if php -r "exit( (int)! version_compare( '$TRAVIS_PHP_VERSION', '5.6', '>=' ) );"; then wget -O phpunit https://phar.phpunit.de/phpunit-5.7.phar && chmod +x phpunit && mkdir ~/bin && mv -v phpunit ~/bin; fi

php:
  - 5.3
  - 5.4
  - 5.5
  - 5.6
  - 7.0
  - 7.1
env:
  - SYMFONY_VERSION=origin/master

before_script:
  - wget http://getcomposer.org/composer.phar
  - php composer.phar install

script: phpunit --configuration phpunit.travis.xml


================================================
FILE: Command/TestPushCommand.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;

use Symfony\Component\Console\Input\InputArgument,
    Symfony\Component\Console\Input\InputInterface,
    Symfony\Component\Console\Input\InputOption,
    Symfony\Component\Console\Output\OutputInterface;
use RMS\PushNotificationsBundle\Message as PushMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface;

class TestPushCommand extends ContainerAwareCommand
{
    /**
     * @var \Doctrine\ORM\EntityManager
     */
    protected $em;

    /**
     * Configures the console commnad
     *
     * @return void
     */
    protected function configure()
    {
        $this
            ->setName("rms:test-push")
            ->setDescription("Sends a push command to a supplied push token'd device")
            ->addOption("badge", "b", InputOption::VALUE_OPTIONAL, "Badge number (for iOS devices)", 0)
            ->addOption("text", "t", InputOption::VALUE_OPTIONAL, "Text message")
            ->addArgument("service", InputArgument::REQUIRED, "One of 'ios', 'c2dm', 'gcm', 'mac', 'blackberry' or 'windowsphone'")
            ->addArgument("token", InputArgument::REQUIRED, "Authentication token for the service")
            ->addArgument("payload", InputArgument::OPTIONAL, "The payload data to send (JSON)", '{"data": "test"}')
        ;
    }

    /**
     * Main command execution.
     *
     * @param  InputInterface  $input  An InputInterface instance
     * @param  OutputInterface $output An OutputInterface instance
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $token = $input->getArgument("token");
        $service = strtolower($input->getArgument("service"));
        $json_payload = $input->getArgument("payload");
        $payload = json_decode($json_payload, true);

        $tokenLengths = array(
            "ios" => 64,
            "c2dm" => 162,
        );

        if (isset($tokenLengths[$service]) && strlen($token) != $tokenLengths[$service]) {
            $output->writeln("<error>Token should be " . $tokenLengths[$service] . "chars long, not " . strlen($token) . "</error>");

            return;
        }

        if ($payload == null) {
            throw new \InvalidArgumentException("Invalid JSON payload " . $json_payload);
        }

        $msg = $this->getMessageClass($service);

        if (method_exists($msg, "setAPSBadge")) {
            // Set badge on iOS
            $msg->setAPSBadge((int) $input->getOption("badge"));
        }
        if (method_exists($msg, "setAPSSound")) {
            // Set sound on iOS
            $msg->setAPSSound("default");
        }

        $msg->setDeviceIdentifier($token);
        $msg->setData($payload);

        if ($input->getOption("text")) {
            $msg->setMessage($input->getOption("text"));
        }

        $result = $this->getContainer()->get("rms_push_notifications")->send($msg);
        if ($result) {
            $output->writeln("<comment>Send successful</comment>");
        } else {
            $output->writeln("<error>Send failed</error>");
        }

        $output->writeln("<comment>done</comment>");
    }

    /**
     * Returns a message class based on the supplied os
     *
     * @param  string                    $service The name of the service to return a message for
     * @throws \InvalidArgumentException
     * @return MessageInterface
     */
    protected function getMessageClass($service)
    {
        switch ($service) {
            case "ios":
                return new PushMessage\iOSMessage();
            case "c2dm":
                return new PushMessage\AndroidMessage();
            case "gcm":
                $message = new PushMessage\AndroidMessage();
                $message->setGCM(true);

                return $message;
            case "blackberry":
                return new PushMessage\BlackberryMessage();
            case "mac":
                return new PushMessage\MacMessage();
            case "windowsphone":
                return new PushMessage\WindowsphoneMessage();
            default:
                throw new \InvalidArgumentException("Service '{$service}' not supported presently");
        }
    }
}


================================================
FILE: DependencyInjection/Compiler/AddHandlerPass.php
================================================
<?php

namespace RMS\PushNotificationsBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface,
    Symfony\Component\DependencyInjection\ContainerBuilder,
    Symfony\Component\DependencyInjection\Definition,
    Symfony\Component\DependencyInjection\Reference,
    RMS\PushNotificationsBundle\Device\Types,
    Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;

class AddHandlerPass implements CompilerPassInterface
{
    /**
     * Processes any handlers tagged accordingly
     *
     * @param  ContainerBuilder $container
     * @return void
     */
    public function process(ContainerBuilder $container)
    {
        $service = $container->getDefinition("rms_push_notifications");

        foreach ($container->findTaggedServiceIds("rms_push_notifications.handler") as $id => $attributes) {
            if (!isset($attributes[0]["osType"])) {
                throw new \LogicException("Handler {$id} requires an osType attribute");
            }

            $definition = $container->getDefinition($id);

            // Get reflection class for validate handler
            try {
                $class = $definition->getClass();

                // Class is parameter
                if (strpos($class, '%') === 0) {
                    $class = $container->getParameter(trim($class, '%'));
                }

                $refClass = new \ReflectionClass($class);
            } catch (\ReflectionException $ref) {
                // Class not found or other reflection error
                throw new \RuntimeException(sprintf(
                    'Can\'t compile notification handler by service id "%s".',
                    $id
                ), 0, $ref);
            } catch (ParameterNotFoundException $paramNotFound) {
                // Parameter not found in service container
                throw new \RuntimeException(sprintf(
                    'Can\'t compile notification handler by service id "%s".',
                    $id
                ), 0, $paramNotFound);
            }

            // Required interface
            $requiredInterface = 'RMS\\PushNotificationsBundle\\Service\\OS\\OSNotificationServiceInterface';
            if (!$refClass->implementsInterface($requiredInterface)) {
                throw new \UnexpectedValueException(sprintf(
                   'Notification service "%s" by id "%s" must be implements "%s" interface!' ,
                   $refClass->getName(), $id, $requiredInterface
                ));
            }

            // Add handler to service notifications storage
            $service->addMethodCall("addHandler", array($attributes[0]["osType"], new Reference($id)));
        }
    }
}


================================================
FILE: DependencyInjection/Configuration.php
================================================
<?php

namespace RMS\PushNotificationsBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

class Configuration implements ConfigurationInterface
{
    /**
     * @var \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition
     */
    protected $root;

    /**
     * Generates the configuration tree builder.
     *
     * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $this->root = $treeBuilder->root("rms_push_notifications");

        $this->addAndroid();
        $this->addiOS();
        $this->addMac();
        $this->addBlackberry();
        $this->addWindowsphone();

        return $treeBuilder;
    }

    /**
     * Android configuration
     */
    protected function addAndroid()
    {
        $this->root->
            children()->
                arrayNode("android")->
                    canBeUnset()->
                    children()->

                        scalarNode("timeout")->defaultValue(5)->end()->

                        // WARNING: These 3 fields as they are, outside of the c2dm array
                        // are deprecrated in favour of using the c2dm array configuration
                        // At present these will be overriden by anything supplied
                        // in the c2dm array
                        scalarNode("username")->defaultValue("")->end()->
                        scalarNode("password")->defaultValue("")->end()->
                        scalarNode("source")->defaultValue("")->end()->

                        arrayNode("c2dm")->
                            canBeUnset()->
                            children()->
                                scalarNode("username")->isRequired()->end()->
                                scalarNode("password")->isRequired()->end()->
                                scalarNode("source")->defaultValue("")->end()->
                            end()->
                        end()->
                        arrayNode("gcm")->
                            canBeUnset()->
                            children()->
                                scalarNode("api_key")->isRequired()->cannotBeEmpty()->end()->
                                booleanNode("use_multi_curl")->defaultValue(true)->end()->
                                booleanNode("dry_run")->defaultFalse()->end()->
                            end()->
                        end()->
                    end()->
                end()->
            end()
        ;
    }

    /**
     * iOS configuration
     */
    protected function addiOS()
    {
        $this->addApple("ios");
    }

    /**
     * Mac configuration
     */
    protected function addMac()
    {
        $this->addApple("mac");
    }

    /**
     * Generic Apple Configuration
     */
    private function addApple($os)
    {
        $config = $this->root->
            children()->
                arrayNode($os)->
                    children()->
                        scalarNode("timeout")->defaultValue(60)->end()->
                        booleanNode("sandbox")->defaultFalse()->end()->
                        scalarNode("pem")->cannotBeEmpty()->end()->
                        scalarNode("passphrase")->defaultValue("")->end()->
                        scalarNode('json_unescaped_unicode')->defaultFalse();
                        if (method_exists($config,'info')) {
                            $config = $config->info('PHP >= 5.4.0 and each messaged must be UTF-8 encoding');
                        }
                        $config->end()->
                    end()->
                end()->
            end()
        ;
    }

    /**
     * Blackberry configuration
     */
    protected function addBlackberry()
    {
        $this->root->
            children()->
                arrayNode("blackberry")->
                    children()->
                        scalarNode("timeout")->defaultValue(5)->end()->
                        booleanNode("evaluation")->defaultFalse()->end()->
                        scalarNode("app_id")->isRequired()->cannotBeEmpty()->end()->
                        scalarNode("password")->isRequired()->cannotBeEmpty()->end()->
                    end()->
                end()->
            end()
        ;
    }

    /**
     * Windows Phone configuration
     */
    protected function addWindowsphone()
    {
        $this->root->
            children()->
                arrayNode('windowsphone')->
                    children()->
                        scalarNode("timeout")->defaultValue(5)->end()->
                    end()->
                end()->
            end()
        ;
    }
}


================================================
FILE: DependencyInjection/RMSPushNotificationsExtension.php
================================================
<?php

namespace RMS\PushNotificationsBundle\DependencyInjection;

use Symfony\Component\HttpKernel\DependencyInjection\Extension,
    Symfony\Component\DependencyInjection\ContainerBuilder,
    Symfony\Component\DependencyInjection\Loader\XmlFileLoader,
    Symfony\Component\Config\FileLocator;

class RMSPushNotificationsExtension extends Extension
{
    /**
     * @var ContainerBuilder
     */
    protected $container;

    /**
     * @var string
     */
    protected $kernelRootDir;

    /**
     * Loads any resources/services we need
     *
     * @param  array                                                   $configs
     * @param  \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @return void
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $this->container = $container;
        $this->kernelRootDir = $container->getParameterBag()->get("kernel.root_dir");

        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.xml');

        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $this->setInitialParams();
        if (isset($config["android"])) {
            $this->setAndroidConfig($config);
            $loader->load('android.xml');
        }
        if (isset($config["ios"])) {
            $this->setiOSConfig($config);
            $loader->load('ios.xml');
        }
        if (isset($config["mac"])) {
            $this->setMacConfig($config);
            $loader->load('mac.xml');
        }
        if (isset($config["blackberry"])) {
            $this->setBlackberryConfig($config);
            $loader->load('blackberry.xml');
        }
        if (isset($config['windowsphone'])) {
            $this->setWindowsphoneConfig($config);
            $loader->load('windowsphone.xml');
        }
    }

    /**
     * Initial enabling
     */
    protected function setInitialParams()
    {
        $this->container->setParameter("rms_push_notifications.android.enabled", false);
        $this->container->setParameter("rms_push_notifications.ios.enabled", false);
        $this->container->setParameter("rms_push_notifications.mac.enabled", false);
    }

    /**
     * Sets Android config into container
     *
     * @param array $config
     */
    protected function setAndroidConfig(array $config)
    {
        $this->container->setParameter("rms_push_notifications.android.enabled", true);
        $this->container->setParameter("rms_push_notifications.android.c2dm.enabled", true);

        // C2DM
        $username = $config["android"]["username"];
        $password = $config["android"]["password"];
        $source = $config["android"]["source"];
        $timeout = $config["android"]["timeout"];
        if (isset($config["android"]["c2dm"])) {
            $username = $config["android"]["c2dm"]["username"];
            $password = $config["android"]["c2dm"]["password"];
            $source = $config["android"]["c2dm"]["source"];
        }
        $this->container->setParameter("rms_push_notifications.android.timeout", $timeout);
        $this->container->setParameter("rms_push_notifications.android.c2dm.username", $username);
        $this->container->setParameter("rms_push_notifications.android.c2dm.password", $password);
        $this->container->setParameter("rms_push_notifications.android.c2dm.source", $source);

        // GCM
        $this->container->setParameter("rms_push_notifications.android.gcm.enabled", isset($config["android"]["gcm"]));
        if (isset($config["android"]["gcm"])) {
            $this->container->setParameter("rms_push_notifications.android.gcm.api_key", $config["android"]["gcm"]["api_key"]);
            $this->container->setParameter("rms_push_notifications.android.gcm.use_multi_curl", $config["android"]["gcm"]["use_multi_curl"]);
            $this->container->setParameter('rms_push_notifications.android.gcm.dry_run', $config["android"]["gcm"]["dry_run"]);
        }
    }

    /**
     * Sets iOS config into container
     *
     * @param array $config
     */
    protected function setiOSConfig(array $config)
    {
        $this->setAppleConfig($config, "ios");
    }

    /**
     * Sets Mac config into container
     *
     * @param array $config
     */
    protected function setMacConfig(array $config)
    {
        $this->setAppleConfig($config, "mac");
    }

    /**
     * Sets Apple config into container
     *
     * @param  array             $config
     * @param $os
     * @throws \RuntimeException
     * @throws \LogicException
     */
    protected function setAppleConfig(array $config, $os)
    {
        $supportedAppleOS = array("mac", "ios");
        //Check if the OS is supported
        if (!in_array($os, $supportedAppleOS, true)) {
            throw new \RuntimeException(sprintf('This Apple OS "%s" is not supported', $os));
        }

        $pemFile = null;
        if (isset($config[$os]["pem"])) {
            // If PEM is set, it must be a real file
            if (realpath($config[$os]["pem"])) {
                // Absolute path
                $pemFile = $config[$os]["pem"];
            } elseif (realpath($this->kernelRootDir.DIRECTORY_SEPARATOR.$config[$os]["pem"]) ) {
                // Relative path
                $pemFile = $this->kernelRootDir.DIRECTORY_SEPARATOR.$config[$os]["pem"];
            } else {
                // path isn't valid
                throw new \RuntimeException(sprintf('Pem file "%s" not found.', $config[$os]["pem"]));
            }
        }

        if ($config[$os]['json_unescaped_unicode']) {
            // Not support JSON_UNESCAPED_UNICODE option
            if (!version_compare(PHP_VERSION, '5.4.0', '>=')) {
                throw new \LogicException(sprintf(
                    'Can\'t use JSON_UNESCAPED_UNICODE option. This option can use only PHP Version >= 5.4.0. Your version: %s',
                    PHP_VERSION
                ));
            }
        }

        $this->container->setParameter(sprintf('rms_push_notifications.%s.enabled', $os), true);
        $this->container->setParameter(sprintf('rms_push_notifications.%s.timeout', $os), $config[$os]["timeout"]);
        $this->container->setParameter(sprintf('rms_push_notifications.%s.sandbox', $os), $config[$os]["sandbox"]);
        $this->container->setParameter(sprintf('rms_push_notifications.%s.pem', $os), $pemFile);
        $this->container->setParameter(sprintf('rms_push_notifications.%s.passphrase', $os), $config[$os]["passphrase"]);
        $this->container->setParameter(sprintf('rms_push_notifications.%s.json_unescaped_unicode', $os), (bool) $config[$os]['json_unescaped_unicode']);
    }

    /**
     * Sets Blackberry config into container
     *
     * @param array $config
     */
    protected function setBlackberryConfig(array $config)
    {
        $this->container->setParameter("rms_push_notifications.blackberry.enabled", true);
        $this->container->setParameter("rms_push_notifications.blackberry.timeout", $config["blackberry"]["timeout"]);
        $this->container->setParameter("rms_push_notifications.blackberry.evaluation", $config["blackberry"]["evaluation"]);
        $this->container->setParameter("rms_push_notifications.blackberry.app_id", $config["blackberry"]["app_id"]);
        $this->container->setParameter("rms_push_notifications.blackberry.password", $config["blackberry"]["password"]);
    }

    protected function setWindowsphoneConfig(array $config)
    {
        $this->container->setParameter("rms_push_notifications.windowsphone.enabled", true);
        $this->container->setParameter("rms_push_notifications.windowsphone.timeout", $config["windowsphone"]["timeout"]);
    }
}


================================================
FILE: Device/Types.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Device;

class Types
{
    const OS_ANDROID_C2DM = "rms_push_notifications.os.android.c2dm";
    const OS_ANDROID_GCM = "rms_push_notifications.os.android.gcm";
    const OS_IOS = "rms_push_notifications.os.ios";
    const OS_MAC = "rms_push_notifications.os.mac";
    const OS_BLACKBERRY = "rms_push_notifications.os.blackberry";
    const OS_WINDOWSMOBILE = "rms_push_notifications.os.windowsmobile";
    const OS_WINDOWSPHONE = "rms_push_notifications.os.windowsphone";
}


================================================
FILE: Device/iOS/Feedback.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Device\iOS;

class Feedback
{
    public $timestamp;
    public $tokenLength;
    public $uuid;

    /**
     * Unpacks the APNS data into the required fields
     *
     * @param $data
     * @return \RMS\PushNotificationsBundle\Device\iOS\Feedback
     */
    public function unpack($data)
    {
        $token = unpack("N1timestamp/n1length/H*token", $data);
        $this->timestamp = $token["timestamp"];
        $this->tokenLength = $token["length"];
        $this->uuid = $token["token"];

        return $this;
    }
}


================================================
FILE: Exception/InvalidMessageTypeException.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Exception;

class InvalidMessageTypeException extends \RuntimeException
{
}


================================================
FILE: LICENSE
================================================
Copyright (c) 2012-2015 Rich Sage

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: Message/AndroidMessage.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Message;

use RMS\PushNotificationsBundle\Device\Types;

class AndroidMessage implements MessageInterface
{
    const DEFAULT_COLLAPSE_KEY = 1;

    /**
     * String message
     *
     * @var string
     */
    protected $message = "";

    /**
     * The data to send in the message
     *
     * @var array
     */
    protected $data = array();

    /**
     * Identifier of the target device
     *
     * @var string
     */
    protected $identifier = "";

    /**
     * Collapse key for data
     *
     * @var int
     */
    protected $collapseKey = self::DEFAULT_COLLAPSE_KEY;

    /**
     * Whether this is a GCM message
     *
     * @var bool
     */
    protected $isGCM = false;

    /**
     * A collection of device identifiers that the message
     * is intended for. GCM use only
     *
     * @var array
     */
    protected $allIdentifiers = array();

    /**
     * Options for GCM messages
     *
     * @var array
     */
    protected $gcmOptions = array();

    /**
     * Sets the string message
     *
     * @param $message
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * Returns the string message
     *
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * Sets the data. For Android, this is any custom data to use
     *
     * @param array $data The custom data to send
     */
    public function setData($data)
    {
        $this->data = (is_array($data) ? $data : array($data));
    }

    /**
     * Returns any custom data
     *
     * @return array
     */
    public function getData()
    {
        return array_merge(array('message' => $this->getMessage()), $this->data);
    }

    /**
     * Gets the message body to send
     * This is primarily used in C2DM
     *
     * @return array
     */
    public function getMessageBody()
    {
        $data = array(
            "registration_id" => $this->identifier,
            "collapse_key"    => $this->collapseKey,
            "data.message"    => $this->message,
        );
        if (!empty($this->data)) {
            $data = array_merge($data, $this->data);
        }

        return $data;
    }

    /**
     * Sets the identifier of the target device, eg UUID or similar
     *
     * @param $identifier
     */
    public function setDeviceIdentifier($identifier)
    {
        $this->identifier = $identifier;
        $this->allIdentifiers = array($identifier => $identifier);
    }

    /**
     * Returns the target OS for this message
     *
     * @return string
     */
    public function getTargetOS()
    {
        return ($this->isGCM ? Types::OS_ANDROID_GCM : Types::OS_ANDROID_C2DM);
    }

    /**
     * Returns the target device identifier
     *
     * @return string
     */
    public function getDeviceIdentifier()
    {
        return $this->identifier;
    }

    /**
     * Android-specific
     * Returns the collapse key
     *
     * @return int
     */
    public function getCollapseKey()
    {
        return $this->collapseKey;
    }

    /**
     * Android-specific
     * Sets the collapse key
     *
     * @param $collapseKey
     */
    public function setCollapseKey($collapseKey)
    {
        $this->collapseKey = $collapseKey;
    }

    /**
     * Set whether this is a GCM message
     * (default false)
     *
     * @param $gcm
     */
    public function setGCM($gcm)
    {
        $this->isGCM = !!$gcm;
    }

    /**
     * Returns whether this is a GCM message
     *
     * @return mixed
     */
    public function isGCM()
    {
        return $this->isGCM;
    }

    /**
     * Returns an array of device identifiers
     * Not used in C2DM
     *
     * @return mixed
     */
    public function getGCMIdentifiers()
    {
        return array_values($this->allIdentifiers);
    }

    /**
     * Adds a device identifier to the GCM list
     * @param string $identifier
     */
    public function addGCMIdentifier($identifier)
    {
        $this->allIdentifiers[$identifier] = $identifier;
    }

    /**
     * Sets the GCM list
     * @param array $allIdentifiers
     */
    public function setAllIdentifiers($allIdentifiers) {
        $this->allIdentifiers = array_combine($allIdentifiers, $allIdentifiers);
    }

    /**
     * Sets GCM options
     * @param array $options
     */
    public function setGCMOptions($options)
    {
        $this->gcmOptions = $options;
    }

    /**
     * Returns GCM options
     *
     * @return array
     */
    public function getGCMOptions()
    {
        return $this->gcmOptions;
    }
}


================================================
FILE: Message/AppleMessage.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Message;

class AppleMessage implements MessageInterface
{
    /**
     * Custom data for the APS body
     *
     * @var array
     */
    protected $customData = array();

    /**
     * Device identifier
     *
     * @var null
     */
    protected $identifier = null;

    /**
     * The APS core body
     *
     * @var array
     */
    protected $apsBody = array();

    /**
     * Expiration date (UTC)
     *
     * A fixed UNIX epoch date expressed in seconds (UTC) that identifies when the notification is no longer valid and can be discarded.
     * If the expiry value is non-zero, APNs tries to deliver the notification at least once.
     * Specify zero to request that APNs not store the notification at all.
     *
     * @var int
     */
    protected $expiry = 0;

    /**
     * Device push magic token
     *
     * @var string
     */
    protected $pushMagicToken = '';

    /**
     * Device token
     *
     * @var string
     */
    protected $token = '';

    /**
     * Whether this is a MDM message or not
     *
     * @var bool
     */
    protected $isMdmMessage = false;

    /**
     * Class constructor
     */
    public function __construct($identifier = NULL)
    {
        $this->apsBody = array(
            "aps" => array(
            ),
        );

        if ($identifier !== NULL) {
            $this->identifier = $identifier;
        }
    }

    /**
     * Sets the message. For iOS, this is the APS alert message
     *
     * @param $message
     */
    public function setMessage($message)
    {
        $this->apsBody["aps"]["alert"] = $message;
    }

    /**
     * Sets any custom data for the APS body
     *
     * @param array $data
     */
    public function setData($data)
    {
        if (!is_array($data)) {
            throw new \InvalidArgumentException(sprintf('Messages custom data must be array, "%s" given.', gettype($data)));
        }

        if (array_key_exists("aps", $data)) {
            unset($data["aps"]);
        }

        foreach ($data as $key => $value) {
            $this->addCustomData($key, $value);
        }

        return $this;
    }

    /**
     * Add custom data
     *
     * @param string $key
     * @param mixed  $value
     */
    public function addCustomData($key, $value)
    {
        if ($key == 'aps') {
            throw new \LogicException('Can\'t replace "aps" data. Please call to setMessage, if your want replace message text.');
        }

        if (is_object($value)) {
            if (interface_exists('JsonSerializable') && !$value instanceof \stdClass && !$value instanceof \JsonSerializable) {
                throw new \InvalidArgumentException(sprintf(
                    'Object %s::%s must be implements JsonSerializable interface for next serialize data.',
                    get_class($value), spl_object_hash($value)
                ));
            }
        }

        $this->customData[$key] = $value;

        return $this;
    }

    /**
     * Sets the identifier of the target device, eg UUID or similar
     *
     * @param $identifier
     */
    public function setDeviceIdentifier($identifier)
    {
        $this->identifier = $identifier;
    }

    /**
     * Gets the full message body to send to APN
     *
     * @return array
     */
    public function getMessageBody()
    {
        $payloadBody = $this->apsBody;
        if (!empty($this->customData)) {
            $payloadBody = array_merge($payloadBody, $this->customData);
        }

        return $payloadBody;
    }

    /**
     * Returns the device identifier
     *
     * @return null|string
     */
    public function getDeviceIdentifier()
    {
        return $this->identifier;
    }

    /**
     * Returns the target OS for this message
     * Must be implemented by subclass
     *
     * @return string
     */
    public function getTargetOS()
    {
        return "";
    }

    /**
     * iOS-specific
     * Sets the APS sound
     *
     * @param string $sound The sound to use. Use 'default' to use the built-in default
     */
    public function setAPSSound($sound)
    {
        $this->apsBody["aps"]["sound"] = $sound;
    }

    /**
     * iOS-specific
     * Sets the APS badge count
     *
     * @param integer $badge The badge count to display
     */
    public function setAPSBadge($badge)
    {
        $this->apsBody["aps"]["badge"] = (int) $badge;
    }

    /**
     * iOS-specific
     * Sets the APS content available flag, used to transform the notification into remote-notification
     * and trigger the "didReceiveRemoteNotification: fetchCompletionHandler:" method on iOS apps
     *
     * @param string $contentAvailable The flag to set the content-available option, for example set it to 1.
     */
    public function setAPSContentAvailable($contentAvailable)
    {
        $this->apsBody["aps"]["content-available"] = $contentAvailable;
    }

    /**
     * iOS-specific
     * Sets the APS category
     *
     * @param string $category The notification category
     */
    public function setCategory($category)
    {
        $this->apsBody["aps"]["category"] = $category;
    }

    /**
     * iOS-specific
     * Sets the APS mutable-content attribute
     *
     * @param bool $mutableContent 
     */
    public function setMutableContent($mutableContent)
    {
        $this->apsBody["aps"]["mutable-content"] = $mutableContent ? 1 : 0;
    }

    /**
     * Set expiry of message
     *
     * @param int $expiry
     */
    public function setExpiry($expiry)
    {
        $this->expiry = $expiry;
    }

    /**
     * Get expiry of message
     *
     * @return int
     */
    public function getExpiry()
    {
        return $this->expiry;
    }

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

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

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

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

    /**
     * @param null|bool $isMdmMessage
     *
     * @return bool|null
     */
    public function isMdmMessage($isMdmMessage = null)
    {
        if ($isMdmMessage === null) {
            return $this->isMdmMessage;
        }

        $this->isMdmMessage = (bool) $isMdmMessage;
    }
}


================================================
FILE: Message/BlackberryMessage.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Message;

use RMS\PushNotificationsBundle\Device\Types;

class BlackberryMessage implements MessageInterface
{
    /**
     * The data to send in the message
     *
     * @var mixed
     */
    protected $data = null;

    /**
     * Identifier of the target device
     *
     * @var string
     */
    protected $identifier = "";

    /**
     * Sets the message
     * For Blackberry, this is the same as the data
     *
     * @param $message
     */
    public function setMessage($message)
    {
        $this->setData($message);
    }

    /**
     * Sets the data. For Blackberry, this is any data required
     *
     * @param array $data The custom data to send
     */
    public function setData($data)
    {
        $this->data = $data;
    }

    /**
     * Gets the message body to send
     * For Blackberry, this is just our data object
     *
     * @return array
     */
    public function getMessageBody()
    {
        return $this->data;
    }

    /**
     * Sets the identifier of the target device, eg UUID or similar
     *
     * @param $identifier
     */
    public function setDeviceIdentifier($identifier)
    {
        $this->identifier = $identifier;
    }

    /**
     * Returns the target OS for this message
     *
     * @return string
     */
    public function getTargetOS()
    {
        return Types::OS_BLACKBERRY;
    }

    /**
     * Returns the target device identifier
     *
     * @return string
     */
    public function getDeviceIdentifier()
    {
        return $this->identifier;
    }
}


================================================
FILE: Message/MacMessage.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Message;

use RMS\PushNotificationsBundle\Device\Types;

class MacMessage extends AppleMessage
{
    /**
     * Returns the target OS for this message
     *
     * @return string
     */
    public function getTargetOS()
    {
        return Types::OS_MAC;
    }
}


================================================
FILE: Message/MessageInterface.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Message;

interface MessageInterface
{
    public function setMessage($message);

    public function setData($data);

    public function setDeviceIdentifier($identifier);

    public function getMessageBody();

    public function getDeviceIdentifier();

    public function getTargetOS();
}


================================================
FILE: Message/WindowsphoneMessage.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Message;

use RMS\PushNotificationsBundle\Device\Types;

class WindowsphoneMessage implements MessageInterface
{
    const TYPE_TOAST = 'toast';

    protected static $notificationClass = array(
        self::TYPE_TOAST => 2
    );

    protected $identifier;

    protected $text1 = '';

    protected $text2 = '';

    protected $target;

    public function __construct()
    {
        $this->target = self::TYPE_TOAST;
    }

    public function getTargetOS()
    {
        return Types::OS_WINDOWSPHONE;
    }

    public function getDeviceIdentifier()
    {
        return $this->identifier;
    }

    public function setDeviceIdentifier($identifier)
    {
        $this->identifier = $identifier;
    }

    public function getMessageBody()
    {
        return array(
            'text1' => $this->text1,
            'text2' => $this->text2
        );
    }

    public function setMessage($message)
    {
        $this->text2 = $message;
    }

    public function setData($data)
    {
        // Not implemented yet
    }

    public function getTarget()
    {
        return $this->target;
    }

    public function getNotificationClass()
    {
        return static::$notificationClass[$this->getTarget()];
    }
}


================================================
FILE: Message/iOSMessage.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Message;

use RMS\PushNotificationsBundle\Device\Types;

class iOSMessage extends AppleMessage
{
    /**
     * Returns the target OS for this message
     *
     * @return string
     */
    public function getTargetOS()
    {
        return Types::OS_IOS;
    }
}


================================================
FILE: README.md
================================================
# RMSPushNotificationsBundle ![](https://secure.travis-ci.org/richsage/RMSPushNotificationsBundle.png)

A bundle to allow sending of push notifications to mobile devices.  Currently supports Android (C2DM, GCM), Blackberry and iOS devices.

## Installation

To use this bundle in your Symfony2 project add the following to your `composer.json`:

    {
        "require": {
            // ...
            "richsage/rms-push-notifications-bundle": "dev-master"
        }
    }

and enable it in your kernel:

    <?php
    // app/AppKernel.php

    public function registerBundles()
    {
        $bundles = array(
            // ...
            new RMS\PushNotificationsBundle\RMSPushNotificationsBundle(),
        );
    }

NOTE: If you are still using Symfony 2.0, please use the `symfony2.0` branch.

## Configuration

Configuration options available are as follows. Note that the specific services will
only be available if you provide configuration respectively for them.

    rms_push_notifications:
      android:
          timeout: 5 # Seconds to wait for connection timeout, default is 5
          c2dm:
              username: <string_android_c2dm_username>
              password: <string_android_c2dm_password>
              source: <string_android_c2dm_source>
          gcm:
              api_key: <string_android_gcm_api_key> # This is titled "Server Key" when creating it
              use_multi_curl: <boolean_android_gcm_use_multi_curl> # default is true
              dry_run: <bool_use_gcm_dry_run>
      ios:
          timeout: 60 # Seconds to wait for connection timeout, default is 60
          sandbox: <bool_use_apns_sandbox>
          pem: <path_apns_certificate> # can be absolute or relative path (from app directory)
          passphrase: <string_apns_certificate_passphrase>
      mac:
          timeout: 60 # Seconds to wait for connection timeout, default is 60
          sandbox: <bool_use_apns_sandbox>
          pem: <path_apns_certificate>
          passphrase: <string_apns_certificate_passphrase>
      blackberry:
          timeout: 5 # Seconds to wait for connection timeout, default is 5
          evaluation: <bool_bb_evaluation_mode>
          app_id: <string_bb_app_id>
          password: <string_bb_password>
      windowsphone:
          timeout: 5 # Seconds to wait for connection timeout, default is 5

NOTE: If you are using Windows, you may need to set the Android GCM `use_multi_curl` flag to false for GCM messages to be sent correctly.

Timeout defaults are the defaults from prior to the introduction of this configuration value.

## Usage

A little example of how to push your first message to an iOS device, we'll assume that you've set up the configuration correctly:

    use RMS\PushNotificationsBundle\Message\iOSMessage;

    class PushDemoController extends Controller
    {
        public function pushAction()
        {
            $message = new iOSMessage();
            $message->setMessage('Oh my! A push notification!');
            $message->setDeviceIdentifier('test012fasdf482asdfd63f6d7bc6d4293aedd5fb448fe505eb4asdfef8595a7');

            $this->container->get('rms_push_notifications')->send($message);

            return new Response('Push notification send!');
        }
    }

The send method will detect the type of message so if you'll pass it an `AndroidMessage` it will automatically send it through the C2DM/GCM servers, and likewise for Mac and Blackberry.

## Android messages

Since both C2DM and GCM are still available, the `AndroidMessage` class has a small flag on it to toggle which service to send it to.  Use as follows:

    use RMS\PushNotificationsBundle\Message\AndroidMessage;

    $message = new AndroidMessage();
    $message->setGCM(true);

to send as a GCM message rather than C2DM.

## iOS Feedback service

The Apple Push Notification service also exposes a Feedback service where you can get information about failed push notifications - see [here](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW3) for further details.

This service is available within the bundle.  The following code demonstrates how you can retrieve data from the service:

    $feedbackService = $container->get("rms_push_notifications.ios.feedback");
    $uuids = $feedbackService->getDeviceUUIDs();

Here, `$uuids` contains an array of [Feedback](https://github.com/richsage/RMSPushNotificationsBundle/blob/master/Device/iOS/Feedback.php) objects, with timestamp, token length and the device UUID all populated.

Apple recommend you poll this service daily.

## Windows Phone - Toast support

The bundle has beta support for Windows Phone, and supports the Toast notification. Use the `WindowsphoneMessage` message class to send accordingly.

# Thanks

Firstly, thanks to all contributors to this bundle!

![](https://www.jetbrains.com/phpstorm/documentation/docs/logo_phpstorm.png)

Secondly, thanks to [JetBrains](http://www.jetbrains.com) for their sponsorship of an open-source [PhpStorm](https://www.jetbrains.com/phpstorm/) licence for this project.


================================================
FILE: RMSPushNotificationsBundle.php
================================================
<?php

namespace RMS\PushNotificationsBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use RMS\PushNotificationsBundle\DependencyInjection\Compiler\AddHandlerPass;

class RMSPushNotificationsBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new AddHandlerPass());
    }
}


================================================
FILE: Resources/config/android.xml
================================================
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="rms_push_notifications.android.c2dm.class">RMS\PushNotificationsBundle\Service\OS\AndroidNotification</parameter>
        <parameter key="rms_push_notifications.android.gcm.class">RMS\PushNotificationsBundle\Service\OS\AndroidGCMNotification</parameter>
    </parameters>

    <services>

        <!-- Android (C2DM) -->
        <service id="rms_push_notifications.android.c2dm" class="%rms_push_notifications.android.c2dm.class%" public="false">
            <argument>%rms_push_notifications.android.c2dm.username%</argument>
            <argument>%rms_push_notifications.android.c2dm.password%</argument>
            <argument>%rms_push_notifications.android.c2dm.source%</argument>
            <argument>%rms_push_notifications.android.timeout%</argument>
            <tag name="rms_push_notifications.handler" osType="rms_push_notifications.os.android.c2dm" />
        </service>

        <!-- Android (GCM) -->
        <service id="rms_push_notifications.android.gcm" class="%rms_push_notifications.android.gcm.class%" public="false">
            <argument>%rms_push_notifications.android.gcm.api_key%</argument>
            <argument>%rms_push_notifications.android.gcm.use_multi_curl%</argument>
            <argument>%rms_push_notifications.android.timeout%</argument>
            <argument type="service" id="logger" />
            <argument>null</argument>
            <argument>%rms_push_notifications.android.gcm.dry_run%</argument>
            <tag name="rms_push_notifications.handler" osType="rms_push_notifications.os.android.gcm" />
        </service>

    </services>

</container>


================================================
FILE: Resources/config/blackberry.xml
================================================
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="rms_push_notifications.blackberry.class">RMS\PushNotificationsBundle\Service\OS\BlackberryNotification</parameter>
    </parameters>

    <services>

        <!-- Blackberry -->
        <service id="rms_push_notifications.blackberry" class="%rms_push_notifications.blackberry.class%" public="false">
            <argument>%rms_push_notifications.blackberry.evaluation%</argument>
            <argument>%rms_push_notifications.blackberry.app_id%</argument>
            <argument>%rms_push_notifications.blackberry.password%</argument>
            <argument>%rms_push_notifications.blackberry.timeout%</argument>
            <argument type="service" id="logger" />
            <tag name="rms_push_notifications.handler" osType="rms_push_notifications.os.blackberry" />
        </service>

    </services>

</container>


================================================
FILE: Resources/config/ios.xml
================================================
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="rms_push_notifications.ios.class">RMS\PushNotificationsBundle\Service\OS\AppleNotification</parameter>
    </parameters>

    <services>

        <!-- iOS-->
        <service id="rms_push_notifications.ios" class="%rms_push_notifications.ios.class%" public="false">
            <argument>%rms_push_notifications.ios.sandbox%</argument>
            <argument>%rms_push_notifications.ios.pem%</argument>
            <argument>%rms_push_notifications.ios.passphrase%</argument>
            <argument>%rms_push_notifications.ios.json_unescaped_unicode%</argument>
            <argument>%rms_push_notifications.ios.timeout%</argument>
            <argument>%kernel.cache_dir%</argument>
            <argument type="service" id="rms_push_notifications.event_listener" />
            <argument type="service" id="logger" />
            <tag name="rms_push_notifications.handler" osType="rms_push_notifications.os.ios" />
        </service>

        <!-- iOS Feedback requests -->
        <service id="rms_push_notifications.ios.feedback" class="%rms_push_notifications.ios.feedback.class%">
            <argument>%rms_push_notifications.ios.sandbox%</argument>
            <argument>%rms_push_notifications.ios.pem%</argument>
            <argument>%rms_push_notifications.ios.passphrase%</argument>
            <argument>%rms_push_notifications.ios.timeout%</argument>
        </service>


    </services>

</container>


================================================
FILE: Resources/config/mac.xml
================================================
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="rms_push_notifications.mac.class">RMS\PushNotificationsBundle\Service\OS\AppleNotification</parameter>
    </parameters>

    <services>

        <!-- Mac-->
        <service id="rms_push_notifications.mac" class="%rms_push_notifications.mac.class%" public="false">
            <argument>%rms_push_notifications.mac.sandbox%</argument>
            <argument>%rms_push_notifications.mac.pem%</argument>
            <argument>%rms_push_notifications.mac.passphrase%</argument>
            <argument>%rms_push_notifications.mac.json_unescaped_unicode%</argument>
            <argument>%rms_push_notifications.mac.timeout%</argument>
            <argument>%kernel.cache_dir%</argument>
            <argument type="service" id="rms_push_notifications.event_listener" />
            <argument type="service" id="logger" />
            <tag name="rms_push_notifications.handler" osType="rms_push_notifications.os.mac" />
        </service>
    </services>

</container>


================================================
FILE: Resources/config/services.xml
================================================
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="rms_push_notifications.class">RMS\PushNotificationsBundle\Service\Notifications</parameter>
        <parameter key="rms_push_notifications.android.class">RMS\PushNotificationsBundle\Service\OS\AndroidNotification</parameter>
        <parameter key="rms_push_notifications.ios.class">RMS\PushNotificationsBundle\Service\OS\AppleNotification</parameter>
        <parameter key="rms_push_notifications.ios.feedback.class">RMS\PushNotificationsBundle\Service\iOSFeedback</parameter>
        <parameter key="rms_push_notifications.mac.class">RMS\PushNotificationsBundle\Service\OS\AppleNotification</parameter>
        <parameter key="rms_push_notifications.event_listener.class">RMS\PushNotificationsBundle\Service\EventListener</parameter>
    </parameters>

    <services>

        <!-- main notification service -->
        <service id="rms_push_notifications" class="%rms_push_notifications.class%">
        </service>

        <service id="rms_push_notifications.event_listener" class="%rms_push_notifications.event_listener.class%">
            <tag name="kernel.event_listener" event="kernel.terminate" method="onKernelTerminate" />
        </service>
    </services>

</container>


================================================
FILE: Resources/config/windowsphone.xml
================================================
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="rms_push_notifications.windowsphone.class">RMS\PushNotificationsBundle\Service\OS\MicrosoftNotification</parameter>
    </parameters>

    <services>

        <!-- Windows Phone -->
        <service id="rms_push_notifications.windowsphone" class="%rms_push_notifications.windowsphone.class%" public="false">
            <argument>%rms_push_notifications.windowsphone.timeout%</argument>
            <argument type="service" id="logger" />
            <tag name="rms_push_notifications.handler" osType="rms_push_notifications.os.windowsphone" />
        </service>

    </services>

</container>


================================================
FILE: Service/EventListener.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Service;


class EventListener {

    /**
     * @var EventListenerInterface[]
     */
    protected $listeners = array();

    /**
     * @param EventListenerInterface $listener
     */
    public function addListener (EventListenerInterface $listener) {
        $this->listeners[] = $listener;
    }

    /**
     * Call onKernelTerminate on every listener
     */
    public function onKernelTerminate () {
        foreach ($this->listeners as $listener) {
            $listener->onKernelTerminate();
        }
    }
}


================================================
FILE: Service/EventListenerInterface.php
================================================
<?php
namespace RMS\PushNotificationsBundle\Service;


interface EventListenerInterface {

    public function onKernelTerminate ();
}

================================================
FILE: Service/Notifications.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Service;

use RMS\PushNotificationsBundle\Device\Types;
use RMS\PushNotificationsBundle\Message\MessageInterface;
use RMS\PushNotificationsBundle\Service\OS\AppleNotification;

class Notifications
{
    /**
     * Array of handlers
     *
     * @var array
     */
    protected $handlers = array();

    /**
     * Constructor
     */
    public function __construct()
    {
    }

    /**
     * Sends a message to a device, identified by
     * the OS and the supplied device token
     *
     * @param  \RMS\PushNotificationsBundle\Message\MessageInterface $message
     * @throws \RuntimeException
     * @return bool
     */
    public function send(MessageInterface $message)
    {
        if (!$this->supports($message->getTargetOS())) {
            throw new \RuntimeException("OS type {$message->getTargetOS()} not supported");
        }

        return $this->handlers[$message->getTargetOS()]->send($message);
    }

    /**
     * Adds a handler
     *
     * @param $osType
     * @param $service
     */
    public function addHandler($osType, $service)
    {
        if (!isset($this->handlers[$osType])) {
            $this->handlers[$osType] = $service;
        }
    }

    /**
     * Get responses from handler
     *
     * @param  string            $osType
     * @return array
     * @throws \RuntimeException
     */
    public function getResponses($osType)
    {
        if (!isset($this->handlers[$osType])) {
            throw new \RuntimeException("OS type {$osType} not supported");
        }

        if (!method_exists($this->handlers[$osType], 'getResponses')) {
            throw new \RuntimeException("Handler for OS type {$osType} not supported getResponses() method");
        }

        return $this->handlers[$osType]->getResponses();
    }

    /**
     * Check if target OS is supported
     *
     * @param $targetOS
     *
     * @return bool
     */
    public function supports($targetOS)
    {
        return isset($this->handlers[$targetOS]);
    }


    /**
     * Set Apple Push Notification Service pem as string.
     * Service won't use pem file passed by config anymore.
     *
     * @param $pemContent string
     * @param $passphrase
     */
    public function setAPNSPemAsString($pemContent, $passphrase) {
        if (isset($this->handlers[Types::OS_IOS]) && $this->handlers[Types::OS_IOS] instanceof AppleNotification) {
            /** @var AppleNotification $appleNotification */
            $appleNotification = $this->handlers[Types::OS_IOS];
            $appleNotification->setPemAsString($pemContent, $passphrase);
        }
    }
}


================================================
FILE: Service/OS/AndroidGCMNotification.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Service\OS;

use Psr\Log\LoggerInterface;
use RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException,
    RMS\PushNotificationsBundle\Message\AndroidMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface;
use Buzz\Browser,
    Buzz\Client\AbstractCurl,
    Buzz\Client\Curl,
    Buzz\Client\MultiCurl;

class AndroidGCMNotification implements OSNotificationServiceInterface
{

    /**
     * Whether or not to use the dry run GCM
     *
     * @var bool
     */
    protected $useDryRun = false;

    /**
     * GCM endpoint
     *
     * @var string
     */
    protected $apiURL = "https://android.googleapis.com/gcm/send";

    /**
     * Google GCM API key
     *
     * @var string
     */
    protected $apiKey;

    /**
     * Max registration count
     *
     * @var integer
     */
    protected $registrationIdMaxCount = 1000;

    /**
     * Browser object
     *
     * @var \Buzz\Browser
     */
    protected $browser;

    /**
     * Collection of the responses from the GCM communication
     *
     * @var array
     */
    protected $responses;

    /**
     * Monolog logger
     *
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * Constructor
     *
     * @param string       $apiKey
     * @param bool         $useMultiCurl
     * @param int          $timeout
     * @param LoggerInterface $logger
     * @param AbstractCurl $client (optional)
     * @param bool         $dryRun
     */
    public function __construct($apiKey, $useMultiCurl, $timeout, $logger, AbstractCurl $client = null, $dryRun = false)
    {
        $this->useDryRun = $dryRun;
        $this->apiKey = $apiKey;
        if (!$client) {
            $client = ($useMultiCurl ? new MultiCurl() : new Curl());
        }
        $client->setTimeout($timeout);

        $this->browser = new Browser($client);
        $this->browser->getClient()->setVerifyPeer(false);
        $this->logger = $logger;
    }

    /**
     * Sends the data to the given registration IDs via the GCM server
     *
     * @param  \RMS\PushNotificationsBundle\Message\MessageInterface              $message
     * @throws \RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException
     * @return bool
     */
    public function send(MessageInterface $message)
    {
        if (!$message instanceof AndroidMessage) {
            throw new InvalidMessageTypeException(sprintf("Message type '%s' not supported by GCM", get_class($message)));
        }
        if (!$message->isGCM()) {
            throw new InvalidMessageTypeException("Non-GCM messages not supported by the Android GCM sender");
        }

        $headers = array(
            "Authorization: key=" . $this->apiKey,
            "Content-Type: application/json",
        );
        $data = array_merge(
            $message->getGCMOptions(),
            array("data" => $message->getData())
        );

        if ($this->useDryRun) {
            $data['dry_run'] = true;
        }

        // Perform the calls (in parallel)
        $this->responses = array();
        $gcmIdentifiers = $message->getGCMIdentifiers();

        if (count($message->getGCMIdentifiers()) == 1) {
            $data['to'] = $gcmIdentifiers[0];
            $this->responses[] = $this->browser->post($this->apiURL, $headers, json_encode($data));
        } else {
            // Chunk number of registration IDs according to the maximum allowed by GCM
            $chunks = array_chunk($message->getGCMIdentifiers(), $this->registrationIdMaxCount);

            foreach ($chunks as $registrationIDs) {
                $data['registration_ids'] = $registrationIDs;
                $this->responses[] = $this->browser->post($this->apiURL, $headers, json_encode($data));
            }
        }

        // If we're using multiple concurrent connections via MultiCurl
        // then we should flush all requests
        if ($this->browser->getClient() instanceof MultiCurl) {
            $this->browser->getClient()->flush();
        }

        // Determine success
        foreach ($this->responses as $response) {
            $message = json_decode($response->getContent());
            if ($message === null || $message->success == 0 || $message->failure > 0) {
                if ($message == null) {
                    $this->logger->error($response->getContent());
                } else {
                    foreach ($message->results as $result) {
                        if (isset($result->error)) {
                            $this->logger->error($result->error);
                        }
                    }
                }
                return false;
            }
        }

        return true;
    }

    /**
     * Returns responses
     *
     * @return array
     */
    public function getResponses()
    {
        return $this->responses;
    }
}


================================================
FILE: Service/OS/AndroidNotification.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Service\OS;

use RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException,
    RMS\PushNotificationsBundle\Message\AndroidMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface;
use Buzz\Browser;

class AndroidNotification implements OSNotificationServiceInterface
{
    /**
     * Username for auth
     *
     * @var string
     */
    protected $username;

    /**
     * Password for auth
     *
     * @var string
     */
    protected $password;

    /**
     * The source of the notification
     * eg com.example.myapp
     *
     * @var string
     */
    protected $source;

    /**
     * Timeout in seconds for the connecting client
     *
     * @var int
     */
    protected $timeout;

    /**
     * Authentication token
     *
     * @var string
     */
    protected $authToken;

    /**
     * Constructor
     *
     * @param $username
     * @param $password
     * @param $source
     * @param $timeout
     */
    public function __construct($username, $password, $source, $timeout)
    {
        $this->username = $username;
        $this->password = $password;
        $this->source = $source;
        $this->timeout = $timeout;
        $this->authToken = "";
    }

    /**
     * Sends a C2DM message
     * This assumes that a valid auth token can be obtained
     *
     * @param  \RMS\PushNotificationsBundle\Message\MessageInterface              $message
     * @throws \RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException
     * @return bool
     */
    public function send(MessageInterface $message)
    {
        if (!$message instanceof AndroidMessage) {
            throw new InvalidMessageTypeException(sprintf("Message type '%s' not supported by C2DM", get_class($message)));
        }

        if ($this->getAuthToken()) {
            $headers[] = "Authorization: GoogleLogin auth=" . $this->authToken;
            $data = $message->getMessageBody();

            $buzz = new Browser();
            $buzz->getClient()->setVerifyPeer(false);
            $buzz->getClient()->setTimeout($this->timeout);
            $response = $buzz->post("https://android.apis.google.com/c2dm/send", $headers, http_build_query($data));

            return preg_match("/^id=/", $response->getContent()) > 0;
        }

        return false;
    }

    /**
     * Gets a valid authentication token
     *
     * @return bool
     */
    protected function getAuthToken()
    {
        $data = array(
            "Email"         => $this->username,
            "Passwd"        => $this->password,
            "accountType"   => "HOSTED_OR_GOOGLE",
            "source"        => $this->source,
            "service"       => "ac2dm"
        );

        $buzz = new Browser();
        $buzz->getClient()->setVerifyPeer(false);
        $buzz->getClient()->setTimeout($this->timeout);
        $response = $buzz->post("https://www.google.com/accounts/ClientLogin", array(), http_build_query($data));
        if ($response->getStatusCode() !== 200) {
            return false;
        }

        preg_match("/Auth=([a-z0-9_\-]+)/i", $response->getContent(), $matches);
        $this->authToken = $matches[1];

        return true;
    }
}


================================================
FILE: Service/OS/AppleNotification.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Service\OS;

use Psr\Log\LoggerInterface;
use RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException,
    RMS\PushNotificationsBundle\Message\AppleMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface,
    Symfony\Component\Filesystem\Filesystem,
    RMS\PushNotificationsBundle\Service\EventListenerInterface;
use RMS\PushNotificationsBundle\Service\EventListener;

class AppleNotification implements OSNotificationServiceInterface, EventListenerInterface
{

    /**
     * Whether or not to use the sandbox APNS
     *
     * @var bool
     */
    protected $useSandbox = false;

    /**
     * Path to PEM file
     *
     * @var string
     */
    protected $pemPath;

    /**
     * Passphrase for PEM file
     *
     * @var string
     */
    protected $passphrase;

    /**
     * Content of PEM
     *
     * @var string
     */
    protected $pemContent;

    /**
     * Passphrase for PEM content
     *
     * @var string
     */
    protected $pemContentPassphrase;

    /**
     * Array for streams to APN
     *
     * @var array
     */
    protected $apnStreams;

    /**
     * Array for messages to APN
     *
     * @var array
     */
    protected $messages;

    /**
     * Last used message ID
     *
     * @var int
     */
    protected $lastMessageId;

    /**
     * JSON_UNESCAPED_UNICODE
     *
     * @var boolean
     */
    protected $jsonUnescapedUnicode = FALSE;

    /**
     * Connection timeout
     *
     * @var int
     */
    protected $timeout;

    /**
     * Collection of the responses from the APN
     *
     * @var array
     */
    protected $responses = array();

    /**
     * Cache dir used for cache pem file
     *
     * @var string
     */
    protected $cachedir;

    /**
     * Monolog logger
     *
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * Cache pem filename
     */
    const APNS_CERTIFICATE_FILE = '/rms_push_notifications/apns.pem';

    /**
     * Status code retrieve when APNS server closed the connection
     */
    const APNS_SHUTDOWN_CODE = 10;

    /**
     * Constructor
     *
     * @param bool          $sandbox
     * @param string        $pem
     * @param string        $passphrase
     * @param bool          $jsonUnescapedUnicode
     * @param int           $timeout
     * @param string        $cachedir
     * @param EventListener $eventListener
     * @param LoggerInterface $logger
     */
    public function __construct($sandbox, $pem, $passphrase = "", $jsonUnescapedUnicode = FALSE, $timeout = 60, $cachedir = "", EventListener $eventListener = null, $logger = null)
    {
        $this->useSandbox = $sandbox;
        $this->pemPath = $pem;
        $this->passphrase = $passphrase;
        $this->apnStreams = array();
        $this->messages = array();
        $this->lastMessageId = -1;
        $this->jsonUnescapedUnicode = $jsonUnescapedUnicode;
        $this->timeout = $timeout;
        $this->cachedir = $cachedir;
        $this->logger = $logger;

        if ($eventListener != null) {
            $eventListener->addListener($this);
        }
    }

    /**
     * Set option JSON_UNESCAPED_UNICODE to json encoders
     *
     * @param boolean $jsonUnescapedUnicode
     */
    public function setJsonUnescapedUnicode($jsonUnescapedUnicode)
    {
        $this->jsonUnescapedUnicode = (bool) $jsonUnescapedUnicode;

        return $this;
    }

    /**
     * Send a MDM or notification message
     *
     * @param  \RMS\PushNotificationsBundle\Message\MessageInterface|\RMS\PushNotificationsBundle\Service\OS\MessageInterface $message
     * @throws \RuntimeException
     * @throws \RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException
     * @return bool
     */
    public function send(MessageInterface $message)
    {
        if (!$message instanceof AppleMessage) {
            throw new InvalidMessageTypeException(sprintf("Message type '%s' not supported by APN", get_class($message)));
        }

        $apnURL = "ssl://gateway.push.apple.com:2195";
        if ($this->useSandbox) {
            $apnURL = "ssl://gateway.sandbox.push.apple.com:2195";
        }

        $messageId = ++$this->lastMessageId;

        if ($message->isMdmMessage()) {
            if ($message->getToken() == '') {
                throw new InvalidMessageTypeException(sprintf("Message type '%s' is a MDM message but 'token' is missing", get_class($message)));
            }

            if ($message->getPushMagicToken() == '') {
                throw new InvalidMessageTypeException(sprintf("Message type '%s' is a MDM message but 'pushMagicToken' is missing", get_class($message)));
            }

            $this->messages[$messageId] = $this->createMdmPayload($message->getToken(), $message->getPushMagicToken());
        } else {
            $this->messages[$messageId] = $this->createPayload($messageId, $message->getExpiry(), $message->getDeviceIdentifier(), $message->getMessageBody());
        }

        $errors = $this->sendMessages($messageId, $apnURL);

        return !$errors;
    }

    /**
     * Send all notification messages starting from the given ID
     *
     * @param  int                                                                $firstMessageId
     * @param  string                                                             $apnURL
     * @throws \RuntimeException
     * @throws \RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException
     * @return int
     */
    protected function sendMessages($firstMessageId, $apnURL)
    {
        $errors = array();
        // Loop through all messages starting from the given ID
        $messagesCount = count($this->messages);
        for ($currentMessageId = $firstMessageId; $currentMessageId < $messagesCount; $currentMessageId++) {
            // Send the message
            $result = $this->writeApnStream($apnURL, $this->messages[$currentMessageId]);

            // Check if there is an error result
            if (is_array($result)) {

                // Close the apn stream in case of Shutdown status code.
                if ($result['status'] === self::APNS_SHUTDOWN_CODE) {
                    $this->closeApnStream($apnURL);
                }

                $this->responses[] = $result;
                // Resend all messages that were sent after the failed message
                $this->sendMessages($result['identifier']+1, $apnURL);
                $errors[] = $result;
                if ($this->logger) {
                    $this->logger->error(json_encode($result));
                }
            } else {
                $this->responses[] = true;
            }
        }

        return $errors;
    }

    /**
     * Write data to the apn stream that is associated with the given apn URL
     *
     * @param  string            $apnURL
     * @param  string            $payload
     * @throws \RuntimeException
     * @return mixed
     */
    protected function writeApnStream($apnURL, $payload)
    {
        // Get the correct Apn stream and send data
        $fp = $this->getApnStream($apnURL);
        $response = (strlen($payload) === @fwrite($fp, $payload, strlen($payload)));

        // Check if there is responsedata to read
        $readStreams = array($fp);
        $null = NULL;
        $streamsReadyToRead = @stream_select($readStreams, $null, $null, 1, 0);
        if ($streamsReadyToRead > 0) {
            // Unpack error response data and set as the result
            $response = @unpack("Ccommand/Cstatus/Nidentifier", fread($fp, 6));
            $this->closeApnStream($apnURL);
        }

        // Will contain true if writing succeeded and no error is returned yet
        return $response;
    }

    /**
     * Get an apn stream associated with the given apn URL, create one if necessary
     *
     * @param  string            $apnURL
     * @throws \RuntimeException
     * @return resource
     */
    protected function getApnStream($apnURL)
    {
        if (!isset($this->apnStreams[$apnURL])) {
            // No stream found, setup a new stream
            $ctx = $this->getStreamContext();
            $this->apnStreams[$apnURL] = stream_socket_client($apnURL, $err, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $ctx);
            if (!$this->apnStreams[$apnURL]) {
                throw new \RuntimeException("Couldn't connect to APN server. Error no $err: $errstr");
            }

            // Reduce buffering and blocking
            if (function_exists("stream_set_read_buffer")) {
                stream_set_read_buffer($this->apnStreams[$apnURL], 6);
            }
            stream_set_write_buffer($this->apnStreams[$apnURL], 0);
            stream_set_blocking($this->apnStreams[$apnURL], 0);
        }

        return $this->apnStreams[$apnURL];
    }

    /**
     * Close the apn stream associated with the given apn URL
     *
     * @param string $apnURL
     */
    protected function closeApnStream($apnURL)
    {
        if (isset($this->apnStreams[$apnURL])) {
            // Stream found, close the stream
            fclose($this->apnStreams[$apnURL]);
            unset($this->apnStreams[$apnURL]);
        }
    }

    /**
     * Gets a stream context set up for SSL
     * using our PEM file and passphrase
     *
     * @return resource
     */
    protected function getStreamContext()
    {
        $pem = $this->pemPath;
        $passphrase = $this->passphrase;

        // Create cache pem file if needed
        if (!empty($this->pemContent)) {
            $filename = $this->cachedir . self::APNS_CERTIFICATE_FILE;

            $fs = new Filesystem();
            $fs->mkdir(dirname($filename));
            file_put_contents($filename, $this->pemContent);

            // Now we use this file as pem
            $pem = $filename;
            $passphrase = $this->pemContentPassphrase;
        }

        $ctx = stream_context_create();
        stream_context_set_option($ctx, "ssl", "local_cert", $pem);
        if (strlen($passphrase)) {
            stream_context_set_option($ctx, "ssl", "passphrase", $passphrase);
        }

        return $ctx;
    }

    /**
     * Creates the full payload for the notification
     *
     * @param int    $messageId
     * @param string $expiry
     * @param string $token
     * @param array  $message
     *
     * @return string
     *
     * @throws \LogicException
     * @throws \InvalidArgumentException
     */
    protected function createPayload($messageId, $expiry, $token, $message)
    {
        if ($this->jsonUnescapedUnicode) {
            // Validate PHP version
            if (!version_compare(PHP_VERSION, '5.4.0', '>=')) {
                throw new \LogicException(sprintf(
                    'Can\'t use JSON_UNESCAPED_UNICODE option on PHP %s. Support PHP >= 5.4.0',
                    PHP_VERSION
                ));
            }

            // WARNING:
            // Set otpion JSON_UNESCAPED_UNICODE is violation
            // of RFC 4627
            // Because required validate charsets (Must be UTF-8)

            $encoding = mb_detect_encoding($message['aps']['alert']);
            if ($encoding != 'UTF-8' && $encoding != 'ASCII') {
                throw new \InvalidArgumentException(sprintf(
                    'Message must be UTF-8 encoding, "%s" given.',
                    mb_detect_encoding($message['aps']['alert'])
                ));
            }

            $jsonBody = json_encode($message, JSON_UNESCAPED_UNICODE);
        } else {
            $jsonBody = json_encode($message);
        }

        $token = preg_replace("/[^0-9A-Fa-f]/", "", $token);
        $payload = chr(1) . pack("N", $messageId) . pack("N", $expiry) . pack("n", 32) . pack("H*", $token) . pack("n", strlen($jsonBody)) . $jsonBody;

        return $payload;
    }

    /**
     * Creates a MDM payload
     *
     * @param string $token
     * @param string $magicPushToken
     *
     * @return string
     */
    public function createMdmPayload($token, $magicPushToken)
    {
        $jsonPayload = json_encode(array('mdm' => $magicPushToken));

        $payload = chr(0) . chr(0) . chr(32) . base64_decode($token) . chr(0)  . chr(strlen($jsonPayload)) . $jsonPayload;

        return $payload;
    }

    /**
     * Returns responses
     *
     * @return array
     */
    public function getResponses()
    {
        return $this->responses;
    }

    /**
     * @param $pemContent
     * @param $passphrase
     */
    public function setPemAsString($pemContent, $passphrase) {
        if ($this->pemContent === $pemContent && $this->pemContentPassphrase === $passphrase) {
            return;
        }

        $this->pemContent = $pemContent;
        $this->pemContentPassphrase = $passphrase;

        // for new pem will take affect we need to close existing streams which use cached pem
        $this->closeStreams();
    }

    /**
     * Called on kernel terminate
     */
    public function onKernelTerminate()
    {
        $this->removeCachedPemFile();
        $this->closeStreams();
    }

    /**
     * Remove cache pem file
     */
    private function removeCachedPemFile()
    {
        $fs = new Filesystem();
        $filename = $this->cachedir . self::APNS_CERTIFICATE_FILE;
        if ($fs->exists(dirname($filename))) {
            $fs->remove(dirname($filename));
        }
    }

    /**
     * Close existing streams
     */
    private function closeStreams()
    {
        foreach ($this->apnStreams as $stream) {
            fclose($stream);
        }

        $this->apnStreams = array();
    }
}


================================================
FILE: Service/OS/BlackberryNotification.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Service\OS;

use Psr\Log\LoggerInterface;
use RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException,
    RMS\PushNotificationsBundle\Message\BlackberryMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface;
use Buzz\Browser,
    Buzz\Listener\BasicAuthListener,
    Buzz\Client\Curl;

class BlackberryNotification implements OSNotificationServiceInterface
{
    /**
     * Evaluation mode or not
     *
     * @var string
     */
    protected $evaluation;

    /**
     * App ID
     *
     * @var string
     */
    protected $appID;

    /**
     * Password for auth
     *
     * @var string
     */
    protected $password;

    /**
     * Timeout in seconds for the connecting client
     *
     * @var int
     */
    protected $timeout;

    /**
     * Monolog logger
     *
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * Constructor
     *
     * @param $evaluation
     * @param $appID
     * @param $password
     * @param $timeout
     * @param $logger
     */
    public function __construct($evaluation, $appID, $password, $timeout, $logger)
    {
        $this->evaluation = $evaluation;
        $this->appID = $appID;
        $this->password = $password;
        $this->timeout = $timeout;
        $this->logger = $logger;
    }

    /**
     * Sends a Blackberry Push message
     *
     * @param  \RMS\PushNotificationsBundle\Message\MessageInterface              $message
     * @throws \RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException
     * @return bool
     */
    public function send(MessageInterface $message)
    {
        if (!$message instanceof BlackberryMessage) {
            throw new InvalidMessageTypeException(sprintf("Message type '%s' not supported by Blackberry", get_class($message)));
        }

        return $this->doSend($message);
    }

    /**
     * Does the actual sending
     *
     * @param  \RMS\PushNotificationsBundle\Message\BlackberryMessage $message
     * @return bool
     */
    protected function doSend(BlackberryMessage $message)
    {
        $separator = "mPsbVQo0a68eIL3OAxnm";
        $body = $this->constructMessageBody($message, $separator);
        $browser = new Browser(new Curl());
        $browser->getClient()->setTimeout($this->timeout);
        $listener = new BasicAuthListener($this->appID, $this->password);
        $browser->addListener($listener);

        $url = "https://pushapi.na.blackberry.com/mss/PD_pushRequest";
        if ($this->evaluation) {
            $url = "https://pushapi.eval.blackberry.com/mss/PD_pushRequest";
        }
        $headers = array();
        $headers[] = "Content-Type: multipart/related; boundary={$separator}; type=application/xml";
        $headers[] = "Accept: text/html, *";
        $headers[] = "Connection: Keep-Alive";

        $response = $browser->post($url, $headers, $body);

        return $this->parseResponse($response);
    }

    /**
     * Builds the actual body of the message
     *
     * @param  \RMS\PushNotificationsBundle\Message\BlackberryMessage $message
     * @param $separator
     * @return string
     */
    protected function constructMessageBody(BlackberryMessage $message, $separator)
    {
        $data = "";
        $messageID = microtime(true);

        $data .= "--" . $separator . "\r\n";
        $data .= "Content-Type: application/xml; charset=UTF-8\r\n\r\n";
        $data .= $this->getXMLBody($message, $messageID) . "\r\n";
        $data .= "--" . $separator . "\r\n";
        $data .= "Content-Type: text/plain\r\n";
        $data .= "Push-Message-ID: {$messageID}\r\n\r\n";
        if (is_array($message->getMessageBody())) {
            $data .= json_encode($message->getMessageBody());
        } else {
            $data .= $message->getMessageBody();
        }
        $data .= "\r\n";
        $data .= "--" . $separator . "--\r\n";

        return $data;
    }

    /**
     * Handles and parses the response
     * Returns a value indicating success/fail
     *
     * @param  \Buzz\Message\Response $response
     * @return bool
     */
    protected function parseResponse(\Buzz\Message\Response $response)
    {
        if (null !== $response->getStatusCode() && $response->getStatusCode() != 200) {
            return false;
        }
        $doc = new \DOMDocument();
        $doc->loadXML($response->getContent());
        $elems = $doc->getElementsByTagName("response-result");
        if (!$elems->length) {
            $this->logger->error('Response is empty');
            return false;
        }
        $responseElement = $elems->item(0);
        if ($responseElement->getAttribute("code") != "1001") {
            $this->logger->error($responseElement->getAttribute("code"). ' : '. $responseElement->getAttribute("desc"));
        }

        return ($responseElement->getAttribute("code") == "1001");
    }

    /**
     * Create the XML body that accompanies the actual push data
     *
     * @param $messageID
     * @return string
     */
    private function getXMLBody(BlackberryMessage $message, $messageID)
    {
        $deliverBefore = gmdate('Y-m-d\TH:i:s\Z', strtotime('+5 minutes'));
        $impl = new \DOMImplementation();
        $dtd = $impl->createDocumentType(
            "pap",
            "-//WAPFORUM//DTD PAP 2.1//EN",
            "http://www.openmobilealliance.org/tech/DTD/pap_2.1.dtd"
        );
        $doc = $impl->createDocument("", "", $dtd);

        // Build it centre-out
        $pm = $doc->createElement("push-message");
        $pm->setAttribute("push-id", $messageID);
        $pm->setAttribute("deliver-before-timestamp", $deliverBefore);
        $pm->setAttribute("source-reference", $this->appID);

        $qos = $doc->createElement("quality-of-service");
        $qos->setAttribute("delivery-method", "unconfirmed");
        $add = $doc->createElement("address");
        $add->setAttribute("address-value", $message->getDeviceIdentifier());

        $pm->appendChild($add);
        $pm->appendChild($qos);
        $pap = $doc->createElement("pap");
        $pap->appendChild($pm);
        $doc->appendChild($pap);

        return $doc->saveXML();
    }
}


================================================
FILE: Service/OS/MicrosoftNotification.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Service\OS;

use Psr\Log\LoggerInterface;
use RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException;
use RMS\PushNotificationsBundle\Message\WindowsphoneMessage;
use RMS\PushNotificationsBundle\Message\MessageInterface;
use Buzz\Browser,
    Buzz\Client\Curl;

class MicrosoftNotification implements OSNotificationServiceInterface
{
    /**
     * Browser object
     *
     * @var \Buzz\Browser
     */
    protected $browser;

    /**
     * Monolog logger
     *
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * @param $timeout
     * @param $logger
     */
    public function __construct($timeout, $logger)
    {
        $this->browser = new Browser(new Curl());
        $this->browser->getClient()->setVerifyPeer(false);
        $this->browser->getClient()->setTimeout($timeout);
        $this->logger = $logger;
    }

    public function send(MessageInterface $message)
    {
        if (!$message instanceof WindowsphoneMessage) {
            throw new InvalidMessageTypeException(sprintf("Message type '%s' not supported by MPNS", get_class($message)));
        }

        $headers = array(
            'Content-Type: text/xml',
            'X-WindowsPhone-Target: ' . $message->getTarget(),
            'X-NotificationClass: ' . $message->getNotificationClass()
        );

        $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><wp:Notification xmlns:wp="WPNotification" />');

        $msgBody = $message->getMessageBody();

        if ($message->getTarget() == WindowsphoneMessage::TYPE_TOAST) {
            $toast = $xml->addChild('wp:Toast');
            $toast->addChild('wp:Text1', htmlspecialchars($msgBody['text1'], ENT_XML1|ENT_QUOTES));
            $toast->addChild('wp:Text2', htmlspecialchars($msgBody['text2'], ENT_XML1|ENT_QUOTES));
        }

        $response = $this->browser->post($message->getDeviceIdentifier(), $headers, $xml->asXML());

        if (!$response->isSuccessful()) {
            $this->logger->error($response->getStatusCode(). ' : '. $response->getReasonPhrase());
        }

        return $response->isSuccessful();
    }
}


================================================
FILE: Service/OS/OSNotificationServiceInterface.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Service\OS;

use RMS\PushNotificationsBundle\Message\MessageInterface;

interface OSNotificationServiceInterface
{
    /**
     * Send a notification message
     *
     * @param  \RMS\PushNotificationsBundle\Message\MessageInterface $message
     * @return mixed
     */
    public function send(MessageInterface $message);
}


================================================
FILE: Service/iOSFeedback.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Service;

use RMS\PushNotificationsBundle\Device\iOS\Feedback;

class iOSFeedback
{
    /**
     * Sandbox mode or not
     *
     * @var bool
     */
    protected $sandbox;

    /**
     * Path to PEM file
     *
     * @var string
     */
    protected $pem;

    /**
     * Passphrase for PEM file
     *
     * @var string
     */
    protected $passphrase;

    /**
     * Connection timeout
     *
     * @var int
     */
    protected $timeout;

    /**
     * Constructor
     *
     * @param $sandbox
     * @param $pem
     * @param $passphrase
     * @param $timeout
     */
    public function __construct($sandbox, $pem, $passphrase, $timeout)
    {
        $this->sandbox = $sandbox;
        $this->pem = $pem;
        $this->passphrase = $passphrase;
        $this->timeout = $timeout;
    }

    /**
     * Gets an array of device UUID unregistration details
     * from the APN feedback service
     *
     * @throws \RuntimeException
     * @return array
     */
    public function getDeviceUUIDs()
    {
        if (!strlen($this->pem)) {
            throw new \RuntimeException("PEM not provided");
        }

        $feedbackURL = "ssl://feedback.push.apple.com:2196";
        if ($this->sandbox) {
            $feedbackURL = "ssl://feedback.sandbox.push.apple.com:2196";
        }
        $data = "";

        $ctx = $this->getStreamContext();
        $fp = stream_socket_client($feedbackURL, $err, $errstr, $this->timeout, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
        if (!$fp) {
            throw new \RuntimeException("Couldn't connect to APNS Feedback service. Error no $err: $errstr");
        }
        while (!feof($fp)) {
            $data .= fread($fp, 4096);
        }
        fclose($fp);
        if (!strlen($data)) {
            return array();
        }

        $feedbacks = array();
        $items = str_split($data, 38);
        foreach ($items as $item) {
            $feedback = new Feedback();
            $feedbacks[] = $feedback->unpack($item);
        }

        return $feedbacks;
    }

    /**
     * Gets a stream context set up for SSL
     * using our PEM file and passphrase
     *
     * @return resource
     */
    protected function getStreamContext()
    {
        $ctx = stream_context_create();

        stream_context_set_option($ctx, "ssl", "local_cert", $this->pem);
        if (strlen($this->passphrase)) {
            stream_context_set_option($ctx, "ssl", "passphrase", $this->passphrase);
        }

        return $ctx;
    }

}


================================================
FILE: Tests/DependencyInjection/ConfigurationTest.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Tests\DependencyInjection;

use RMS\PushNotificationsBundle\DependencyInjection\Configuration;
use Symfony\Component\Config\Definition\Processor;

class ConfigurationTest extends \PHPUnit_Framework_TestCase
{
    public function testDefaults()
    {
        $config = $this->process(array());
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testAddingAndroidKeyRequiresValues()
    {
        $arr = array(
            array("android" => "~"),
        );
        $config = $this->process($arr);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testAndroidRequiresUsername()
    {
        $arr = array(
            array(
                "android" => array("c2dm" => array("password" => "foo"))
            ),
        );
        $config = $this->process($arr);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testAndroidRequiresPassword()
    {
        $arr = array(
            array(
                "android" => array("c2dm" => array("username" => "foo"))
            ),
        );
        $config = $this->process($arr);
    }

    public function testOldFullAndroid()
    {
        // NB - this is the deprecated version
        $arr = array(
            array(
                "android" => array("username" => "foo", "password" => "bar", "source" => "123")
            ),
        );
        $config = $this->process($arr);
        $this->assertArrayHasKey("android", $config);
        $this->assertEquals(5, $config["android"]["timeout"]);
        $this->assertEquals("foo", $config["android"]["username"]);
        $this->assertEquals("bar", $config["android"]["password"]);
        $this->assertEquals("123", $config["android"]["source"]);
    }

    public function testNewC2DMIsAllowedWithoutOldBits()
    {
        $arr = array(
            array(
                "android" => array(
                    "c2dm" => array(
                        "username" => "foo",
                        "password" => "bar",
                        "source" => "123"
                    )
                )
            ),
        );
        $config = $this->process($arr);
        $this->assertArrayHasKey("android", $config);
        $this->assertEquals(5, $config["android"]["timeout"]);
        $this->assertArrayHasKey("c2dm", $config["android"]);
        $this->assertEquals("foo", $config["android"]["c2dm"]["username"]);
        $this->assertEquals("bar", $config["android"]["c2dm"]["password"]);
        $this->assertEquals("123", $config["android"]["c2dm"]["source"]);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testGCMRequiresAPIKey()
    {
        $arr = array(
            array(
                "android" => array(
                    "gcm" => array(
                    )
                )
            ),
        );
        $config = $this->process($arr);
    }

    public function testGCMIsOK()
    {
        $arr = array(
            array(
                "android" => array(
                    "gcm" => array(
                        "api_key" => "foo",
                        "use_multi_curl" => true,
                        "dry_run" => false,
                    )
                )
            ),
        );
        $config = $this->process($arr);
        $this->assertEquals("foo", $config["android"]["gcm"]["api_key"]);
        $this->assertFalse($config["android"]["gcm"]["dry_run"]);

        $arr[0]["android"]["gcm"]["dry_run"] = true;
        $config = $this->process($arr);
        $this->assertTrue($config["android"]["gcm"]["dry_run"]);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testAddingiOsKeyRequiresValues()
    {
        $arr = array(
            array("ios" => "~"),
        );
        $config = $this->process($arr);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testiOSRequiresPEM()
    {
        $arr = array(
            array(
                "ios" => array("pem" => "")
            ),
        );
        $config = $this->process($arr);
    }

    public function testFulliOS()
    {
        $arr = array(
            array(
                "ios" => array("sandbox" => false, "pem" => "foo/bar.pem", "passphrase" => "foo")
            ),
        );
        $config = $this->process($arr);
        $this->assertArrayHasKey("ios", $config);
        $this->assertEquals(60, $config["ios"]["timeout"]);
        $this->assertEquals(false, $config["ios"]["sandbox"]);
        $this->assertEquals("foo/bar.pem", $config["ios"]["pem"]);
        $this->assertEquals("foo", $config["ios"]["passphrase"]);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testAddingMacKeyRequiresValues()
    {
        $arr = array(
            array("mac" => "~"),
        );
        $config = $this->process($arr);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testMacRequiresPEM()
    {
        $arr = array(
            array(
                "mac" => array("pem" => "")
            ),
        );
        $config = $this->process($arr);
    }

    public function testFullMac()
    {
        $arr = array(
            array(
                "mac" => array("sandbox" => false, "pem" => "foo/bar.pem", "passphrase" => "foo")
            ),
        );
        $config = $this->process($arr);
        $this->assertArrayHasKey("mac", $config);
        $this->assertEquals(60, $config["mac"]["timeout"]);
        $this->assertEquals(false, $config["mac"]["sandbox"]);
        $this->assertEquals("foo/bar.pem", $config["mac"]["pem"]);
        $this->assertEquals("foo", $config["mac"]["passphrase"]);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testBlackberryRequiresAppID()
    {
        $arr = array(
            array(
                "blackberry" => array("password" => "foo")
            ),
        );
        $config = $this->process($arr);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testBlackberryRequiresPassword()
    {
        $arr = array(
            array(
                "blackberry" => array("app_id" => "foo")
            ),
        );
        $config = $this->process($arr);
    }

    public function testFullBlackberry()
    {
        $arr = array(
            array(
                "blackberry" => array("evaluation" => false, "app_id" => "foo", "password" => "bar")
            ),
        );
        $config = $this->process($arr);
        $this->assertArrayHasKey("blackberry", $config);
        $this->assertFalse($config["blackberry"]["evaluation"]);
        $this->assertEquals(5, $config["blackberry"]["timeout"]);
        $this->assertEquals("foo", $config["blackberry"]["app_id"]);
        $this->assertEquals("bar", $config["blackberry"]["password"]);
    }

    /**
     * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
     */
    public function testAddingWindowsKeyRequiresValues()
    {
        $arr = array(
            array(
                "windowsphone" => "~"
            ),
        );
        $config = $this->process($arr);
    }

    public function testFullWindows()
    {
        $arr = array(
            array(
                "windowsphone" => array("timeout" => 5)
            ),
        );
        $config = $this->process($arr);
        $this->assertArrayHasKey("windowsphone", $config);
        $this->assertEquals(5, $config["windowsphone"]["timeout"]);
    }

    /**
     * Takes in an array of configuration values and returns the processed version
     *
     * @param  array $config
     * @return array
     */
    protected function process($config)
    {
        $processor = new Processor();

        return $processor->processConfiguration(new Configuration(), $config);
    }
}


================================================
FILE: Tests/Message/AndroidMessageTest.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Tests\Message;

use RMS\PushNotificationsBundle\Device\Types,
    RMS\PushNotificationsBundle\Message\AndroidMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface;

class AndroidMessageTest extends \PHPUnit_Framework_TestCase
{
    public function testCreation()
    {
        $msg = new AndroidMessage();
        $this->assertInstanceOf("RMS\PushNotificationsBundle\Message\MessageInterface", $msg);
        $this->assertEquals(Types::OS_ANDROID_C2DM, $msg->getTargetOS());
    }

    public function testCoreBodyGeneratedOK()
    {
        $expected = array(
            "registration_id" => "",
            "collapse_key"    => AndroidMessage::DEFAULT_COLLAPSE_KEY,
            "data.message"    => "",
        );
        $msg = new AndroidMessage();
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testMessageAddedOK()
    {
        $expected = array(
            "registration_id" => "",
            "collapse_key"    => AndroidMessage::DEFAULT_COLLAPSE_KEY,
            "data.message"    => "Foo",
        );
        $msg = new AndroidMessage();
        $msg->setMessage("Foo");
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testNewCollapseKey()
    {
        $expected = array(
            "registration_id" => "",
            "collapse_key"    => 123,
            "data.message"    => "",
        );
        $msg = new AndroidMessage();
        $msg->setCollapseKey(123);
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testRegistrationIDAddedToBody()
    {
        $expected = array(
            "registration_id" => "ABC123",
            "collapse_key"    => AndroidMessage::DEFAULT_COLLAPSE_KEY,
            "data.message"    => "",
        );
        $msg = new AndroidMessage();
        $msg->setDeviceIdentifier("ABC123");
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testCustomData()
    {
        $expected = array(
            "registration_id" => "",
            "collapse_key"    => AndroidMessage::DEFAULT_COLLAPSE_KEY,
            "data.message"    => "",
            "custom"          => array("foo" => "bar"),
        );
        $msg = new AndroidMessage();
        $msg->setData(array("custom" => array("foo" => "bar")));
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testTypeChangesBasedOnGCM()
    {
        $msg = new AndroidMessage();
        $this->assertEquals(Types::OS_ANDROID_C2DM, $msg->getTargetOS());
        $msg->setGCM(true);
        $this->assertEquals(Types::OS_ANDROID_GCM, $msg->getTargetOS());
    }

    public function testSetIdentifierIsSingleEntryInGCMArray()
    {
        $msg = new AndroidMessage();
        $msg->setDeviceIdentifier("foo");
        $this->assertCount(1, $msg->getGCMIdentifiers());
    }

    public function testAddingGCMIdentifiers()
    {
        $msg = new AndroidMessage();
        $msg->addGCMIdentifier("foo");
        $msg->addGCMIdentifier("bar");
        $this->assertCount(2, $msg->getGCMIdentifiers());
    }

    public function testSetMessageIsReturnedInGetData()
    {
        $msg = new AndroidMessage();
        $message = 'Test message';
        $msg->setMessage($message);
        $this->assertEquals(array('message' => $message), $msg->getData());

        $msg->setData(array('id' => 10));
        $this->assertEquals(array('id' => 10, 'message' => $message), $msg->getData());

        $msg->setData(array('message' => 'Other message'));
        $this->assertEquals(array('message' => 'Other message'), $msg->getData());
    }
}


================================================
FILE: Tests/Message/BlackberryMessageTest.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Tests\Message;

use RMS\PushNotificationsBundle\Device\Types,
    RMS\PushNotificationsBundle\Message\BlackberryMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface;

class BlackberryMessageTest extends \PHPUnit_Framework_TestCase
{
    public function testCreation()
    {
        $msg = new BlackberryMessage();
        $this->assertInstanceOf("RMS\PushNotificationsBundle\Message\MessageInterface", $msg);
        $this->assertEquals(Types::OS_BLACKBERRY, $msg->getTargetOS());
    }

    public function testDefaultBody()
    {
        $expected = null;
        $msg = new BlackberryMessage();
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testSettingBody()
    {
        $expected = "Foo";
        $msg = new BlackberryMessage();
        $msg->setMessage("Foo");
        $this->assertEquals($expected, $msg->getMessageBody());
    }
}


================================================
FILE: Tests/Message/MacMessageTest.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Tests\Message;

use RMS\PushNotificationsBundle\Device\Types,
    RMS\PushNotificationsBundle\Message\MacMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface;

class MacMessageTest extends \PHPUnit_Framework_TestCase
{
    public function testCreation()
    {
        $msg = new MacMessage();
        $this->assertInstanceOf("RMS\PushNotificationsBundle\Message\MessageInterface", $msg);
        $this->assertEquals(Types::OS_MAC, $msg->getTargetOS());
    }
}


================================================
FILE: Tests/Message/WindowsphoneMessageTest.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Tests\Message;

use RMS\PushNotificationsBundle\Device\Types,
    RMS\PushNotificationsBundle\Message\WindowsphoneMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface;

class WindowsphoneMessageTest extends \PHPUnit_Framework_TestCase
{
    public function testCreation()
    {
        $msg = new WindowsphoneMessage();
        $this->assertInstanceOf("RMS\PushNotificationsBundle\Message\MessageInterface", $msg);
        $this->assertEquals(Types::OS_WINDOWSPHONE, $msg->getTargetOS());
    }

    public function testDefaultBody()
    {
        $msg = new WindowsphoneMessage();
        $this->assertArrayHasKey("text1", $msg->getMessageBody());
        $this->assertArrayHasKey("text2", $msg->getMessageBody());
    }

    public function testSettingBody()
    {
        $expected = "Foo";
        $msg = new WindowsphoneMessage();
        $msg->setMessage("Foo");
        $msgBody = $msg->getMessageBody();
        $this->assertEquals($expected, $msgBody['text2']);
    }

    public function testDefaultTarget()
    {
        $msg = new WindowsphoneMessage();
        $this->assertEquals(WindowsphoneMessage::TYPE_TOAST, $msg->getTarget());
    }
}


================================================
FILE: Tests/Message/iOSMessageTest.php
================================================
<?php

namespace RMS\PushNotificationsBundle\Tests\Message;

use RMS\PushNotificationsBundle\Device\Types,
    RMS\PushNotificationsBundle\Message\iOSMessage,
    RMS\PushNotificationsBundle\Message\MessageInterface;

class iOSMessageTest extends \PHPUnit_Framework_TestCase
{
    public function testCreation()
    {
        $msg = new iOSMessage();
        $this->assertInstanceOf("RMS\PushNotificationsBundle\Message\MessageInterface", $msg);
        $this->assertEquals(Types::OS_IOS, $msg->getTargetOS());
    }

    public function testCoreBodyGeneratedOK()
    {
        $expected = array(
            "aps" => array(),
        );
        $msg = new iOSMessage();
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testAPSAlertAddedOK()
    {
        $expected = array(
            "aps" => array(
                "alert" => "Foo",
            ),
        );
        $msg = new iOSMessage();
        $msg->setMessage("Foo");
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testAPSBadgeAddedOK()
    {
        $expected = array(
            "aps" => array(
                "badge" => 1,
            ),
        );
        $msg = new iOSMessage();
        $msg->setAPSBadge(1);
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testAPSSoundAddedOK()
    {
        $expected = array(
            "aps" => array(
                "sound" => "default",
            ),
        );
        $msg = new iOSMessage();
        $msg->setAPSSound("default");
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testCustomDataAddedOK()
    {
        $expected = array(
            "aps" => array(),
            "custom" => array("foo" => "bar"),
        );
        $msg = new iOSMessage();
        $msg->setData(array("custom" => array("foo" => "bar")));
        $this->assertEquals($expected, $msg->getMessageBody());
    }

    public function testMutableContentAddOk()
    {
        $expected = array(
            "aps" => array(
                "mutable-content" => 1,
            ),
        );
        $msg = new iOSMessage();
        $msg->setMutableContent(true);
        $this->assertEquals($expected, $msg->getMessageBody());
    }

}


================================================
FILE: Tests/autoload.php.dist
================================================
<?php

$vendorDir = __DIR__ . '/../vendor';
 
if (!@include($vendorDir . '/autoload.php')) {
    die("You must set up the project dependencies, run the following commands:
wget http://getcomposer.org/composer.phar
php composer.phar install
    ");
}


================================================
FILE: Tests/bootstrap.php
================================================
<?php

if (file_exists($file = __DIR__.'/autoload.php')) {
    require_once $file;
} elseif (file_exists($file = __DIR__.'/autoload.php.dist')) {
    require_once $file;
}


================================================
FILE: composer.json
================================================
{
    "name": "richsage/rms-push-notifications-bundle",
    "type": "symfony-bundle",
    "description": "Push notifications/messages for mobile devices",
    "keywords": ["push", "notification", "bundle", "gcm", "c2dm", "ios", "apns", "blackberry", "mpns", "windowsphone"],
    "homepage": "https://github.com/richsage/RMSPushNotificationsBundle",
    "license": "MIT",
    "authors": [
        {
            "name": "Rich Sage",
            "email": "rich.sage@gmail.com",
            "homepage": "http://www.richsage.co.uk/"
        }
    ],
    "require": {
        "php": ">=5.3.0",
        "kriswallsmith/buzz": "*",
        "psr/log": "^1.0"
    },
    "require-dev": {
        "symfony/symfony": "^2.0 || ^3.0"
    },
    "suggest": {
        "symfony/symfony": "To use as a bundle"
    },
    "autoload": {
        "psr-0": { "RMS\\PushNotificationsBundle": "" }
    },
    "target-dir": "RMS/PushNotificationsBundle",
    "extra": {
        "branch-alias": {
            "dev-master": "0.2.x-dev"
        }
    }
}


================================================
FILE: phpunit.travis.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>

<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="false"
         bootstrap="./Tests/bootstrap.php"
>

    <testsuites>
        <testsuite name="PushNotificationsBundle Test Suite">
            <directory>./Tests</directory>
        </testsuite>
    </testsuites>
</phpunit>


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>

<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="false"
         bootstrap="./Tests/bootstrap.php"
>
         
    <testsuites>
        <testsuite name="PushNotificationsBundle Test Suite">
            <directory>./Tests</directory>
        </testsuite>
    </testsuites>
</phpunit>
Download .txt
gitextract_uz_kv258/

├── .gitignore
├── .travis.yml
├── Command/
│   └── TestPushCommand.php
├── DependencyInjection/
│   ├── Compiler/
│   │   └── AddHandlerPass.php
│   ├── Configuration.php
│   └── RMSPushNotificationsExtension.php
├── Device/
│   ├── Types.php
│   └── iOS/
│       └── Feedback.php
├── Exception/
│   └── InvalidMessageTypeException.php
├── LICENSE
├── Message/
│   ├── AndroidMessage.php
│   ├── AppleMessage.php
│   ├── BlackberryMessage.php
│   ├── MacMessage.php
│   ├── MessageInterface.php
│   ├── WindowsphoneMessage.php
│   └── iOSMessage.php
├── README.md
├── RMSPushNotificationsBundle.php
├── Resources/
│   └── config/
│       ├── android.xml
│       ├── blackberry.xml
│       ├── ios.xml
│       ├── mac.xml
│       ├── services.xml
│       └── windowsphone.xml
├── Service/
│   ├── EventListener.php
│   ├── EventListenerInterface.php
│   ├── Notifications.php
│   ├── OS/
│   │   ├── AndroidGCMNotification.php
│   │   ├── AndroidNotification.php
│   │   ├── AppleNotification.php
│   │   ├── BlackberryNotification.php
│   │   ├── MicrosoftNotification.php
│   │   └── OSNotificationServiceInterface.php
│   └── iOSFeedback.php
├── Tests/
│   ├── DependencyInjection/
│   │   └── ConfigurationTest.php
│   ├── Message/
│   │   ├── AndroidMessageTest.php
│   │   ├── BlackberryMessageTest.php
│   │   ├── MacMessageTest.php
│   │   ├── WindowsphoneMessageTest.php
│   │   └── iOSMessageTest.php
│   ├── autoload.php.dist
│   └── bootstrap.php
├── composer.json
├── phpunit.travis.xml
└── phpunit.xml.dist
Download .txt
SYMBOL INDEX (199 symbols across 31 files)

FILE: Command/TestPushCommand.php
  class TestPushCommand (line 14) | class TestPushCommand extends ContainerAwareCommand
    method configure (line 26) | protected function configure()
    method execute (line 46) | protected function execute(InputInterface $input, OutputInterface $out...
    method getMessageClass (line 103) | protected function getMessageClass($service)

FILE: DependencyInjection/Compiler/AddHandlerPass.php
  class AddHandlerPass (line 12) | class AddHandlerPass implements CompilerPassInterface
    method process (line 20) | public function process(ContainerBuilder $container)

FILE: DependencyInjection/Configuration.php
  class Configuration (line 8) | class Configuration implements ConfigurationInterface
    method getConfigTreeBuilder (line 20) | public function getConfigTreeBuilder()
    method addAndroid (line 37) | protected function addAndroid()
    method addiOS (line 80) | protected function addiOS()
    method addMac (line 88) | protected function addMac()
    method addApple (line 96) | private function addApple($os)
    method addBlackberry (line 120) | protected function addBlackberry()
    method addWindowsphone (line 139) | protected function addWindowsphone()

FILE: DependencyInjection/RMSPushNotificationsExtension.php
  class RMSPushNotificationsExtension (line 10) | class RMSPushNotificationsExtension extends Extension
    method load (line 29) | public function load(array $configs, ContainerBuilder $container)
    method setInitialParams (line 66) | protected function setInitialParams()
    method setAndroidConfig (line 78) | protected function setAndroidConfig(array $config)
    method setiOSConfig (line 112) | protected function setiOSConfig(array $config)
    method setMacConfig (line 122) | protected function setMacConfig(array $config)
    method setAppleConfig (line 135) | protected function setAppleConfig(array $config, $os)
    method setBlackberryConfig (line 181) | protected function setBlackberryConfig(array $config)
    method setWindowsphoneConfig (line 190) | protected function setWindowsphoneConfig(array $config)

FILE: Device/Types.php
  class Types (line 5) | class Types

FILE: Device/iOS/Feedback.php
  class Feedback (line 5) | class Feedback
    method unpack (line 17) | public function unpack($data)

FILE: Exception/InvalidMessageTypeException.php
  class InvalidMessageTypeException (line 5) | class InvalidMessageTypeException extends \RuntimeException

FILE: Message/AndroidMessage.php
  class AndroidMessage (line 7) | class AndroidMessage implements MessageInterface
    method setMessage (line 66) | public function setMessage($message)
    method getMessage (line 76) | public function getMessage()
    method setData (line 86) | public function setData($data)
    method getData (line 96) | public function getData()
    method getMessageBody (line 107) | public function getMessageBody()
    method setDeviceIdentifier (line 126) | public function setDeviceIdentifier($identifier)
    method getTargetOS (line 137) | public function getTargetOS()
    method getDeviceIdentifier (line 147) | public function getDeviceIdentifier()
    method getCollapseKey (line 158) | public function getCollapseKey()
    method setCollapseKey (line 169) | public function setCollapseKey($collapseKey)
    method setGCM (line 180) | public function setGCM($gcm)
    method isGCM (line 190) | public function isGCM()
    method getGCMIdentifiers (line 201) | public function getGCMIdentifiers()
    method addGCMIdentifier (line 210) | public function addGCMIdentifier($identifier)
    method setAllIdentifiers (line 219) | public function setAllIdentifiers($allIdentifiers) {
    method setGCMOptions (line 227) | public function setGCMOptions($options)
    method getGCMOptions (line 237) | public function getGCMOptions()

FILE: Message/AppleMessage.php
  class AppleMessage (line 5) | class AppleMessage implements MessageInterface
    method __construct (line 63) | public function __construct($identifier = NULL)
    method setMessage (line 80) | public function setMessage($message)
    method setData (line 90) | public function setData($data)
    method addCustomData (line 113) | public function addCustomData($key, $value)
    method setDeviceIdentifier (line 138) | public function setDeviceIdentifier($identifier)
    method getMessageBody (line 148) | public function getMessageBody()
    method getDeviceIdentifier (line 163) | public function getDeviceIdentifier()
    method getTargetOS (line 174) | public function getTargetOS()
    method setAPSSound (line 185) | public function setAPSSound($sound)
    method setAPSBadge (line 196) | public function setAPSBadge($badge)
    method setAPSContentAvailable (line 208) | public function setAPSContentAvailable($contentAvailable)
    method setCategory (line 219) | public function setCategory($category)
    method setMutableContent (line 230) | public function setMutableContent($mutableContent)
    method setExpiry (line 240) | public function setExpiry($expiry)
    method getExpiry (line 250) | public function getExpiry()
    method setPushMagicToken (line 258) | public function setPushMagicToken($pushMagicToken)
    method getPushMagicToken (line 266) | public function getPushMagicToken()
    method getToken (line 274) | public function getToken()
    method setToken (line 282) | public function setToken($token)
    method isMdmMessage (line 292) | public function isMdmMessage($isMdmMessage = null)

FILE: Message/BlackberryMessage.php
  class BlackberryMessage (line 7) | class BlackberryMessage implements MessageInterface
    method setMessage (line 29) | public function setMessage($message)
    method setData (line 39) | public function setData($data)
    method getMessageBody (line 50) | public function getMessageBody()
    method setDeviceIdentifier (line 60) | public function setDeviceIdentifier($identifier)
    method getTargetOS (line 70) | public function getTargetOS()
    method getDeviceIdentifier (line 80) | public function getDeviceIdentifier()

FILE: Message/MacMessage.php
  class MacMessage (line 7) | class MacMessage extends AppleMessage
    method getTargetOS (line 14) | public function getTargetOS()

FILE: Message/MessageInterface.php
  type MessageInterface (line 5) | interface MessageInterface
    method setMessage (line 7) | public function setMessage($message);
    method setData (line 9) | public function setData($data);
    method setDeviceIdentifier (line 11) | public function setDeviceIdentifier($identifier);
    method getMessageBody (line 13) | public function getMessageBody();
    method getDeviceIdentifier (line 15) | public function getDeviceIdentifier();
    method getTargetOS (line 17) | public function getTargetOS();

FILE: Message/WindowsphoneMessage.php
  class WindowsphoneMessage (line 7) | class WindowsphoneMessage implements MessageInterface
    method __construct (line 23) | public function __construct()
    method getTargetOS (line 28) | public function getTargetOS()
    method getDeviceIdentifier (line 33) | public function getDeviceIdentifier()
    method setDeviceIdentifier (line 38) | public function setDeviceIdentifier($identifier)
    method getMessageBody (line 43) | public function getMessageBody()
    method setMessage (line 51) | public function setMessage($message)
    method setData (line 56) | public function setData($data)
    method getTarget (line 61) | public function getTarget()
    method getNotificationClass (line 66) | public function getNotificationClass()

FILE: Message/iOSMessage.php
  class iOSMessage (line 7) | class iOSMessage extends AppleMessage
    method getTargetOS (line 14) | public function getTargetOS()

FILE: RMSPushNotificationsBundle.php
  class RMSPushNotificationsBundle (line 9) | class RMSPushNotificationsBundle extends Bundle
    method build (line 11) | public function build(ContainerBuilder $container)

FILE: Service/EventListener.php
  class EventListener (line 6) | class EventListener {
    method addListener (line 16) | public function addListener (EventListenerInterface $listener) {
    method onKernelTerminate (line 23) | public function onKernelTerminate () {

FILE: Service/EventListenerInterface.php
  type EventListenerInterface (line 5) | interface EventListenerInterface {
    method onKernelTerminate (line 7) | public function onKernelTerminate ();

FILE: Service/Notifications.php
  class Notifications (line 9) | class Notifications
    method __construct (line 21) | public function __construct()
    method send (line 33) | public function send(MessageInterface $message)
    method addHandler (line 48) | public function addHandler($osType, $service)
    method getResponses (line 62) | public function getResponses($osType)
    method supports (line 82) | public function supports($targetOS)
    method setAPNSPemAsString (line 95) | public function setAPNSPemAsString($pemContent, $passphrase) {

FILE: Service/OS/AndroidGCMNotification.php
  class AndroidGCMNotification (line 14) | class AndroidGCMNotification implements OSNotificationServiceInterface
    method __construct (line 76) | public function __construct($apiKey, $useMultiCurl, $timeout, $logger,...
    method send (line 97) | public function send(MessageInterface $message)
    method getResponses (line 167) | public function getResponses()

FILE: Service/OS/AndroidNotification.php
  class AndroidNotification (line 10) | class AndroidNotification implements OSNotificationServiceInterface
    method __construct (line 56) | public function __construct($username, $password, $source, $timeout)
    method send (line 73) | public function send(MessageInterface $message)
    method getAuthToken (line 99) | protected function getAuthToken()

FILE: Service/OS/AppleNotification.php
  class AppleNotification (line 13) | class AppleNotification implements OSNotificationServiceInterface, Event...
    method __construct (line 129) | public function __construct($sandbox, $pem, $passphrase = "", $jsonUne...
    method setJsonUnescapedUnicode (line 152) | public function setJsonUnescapedUnicode($jsonUnescapedUnicode)
    method send (line 167) | public function send(MessageInterface $message)
    method sendMessages (line 208) | protected function sendMessages($firstMessageId, $apnURL)
    method writeApnStream (line 248) | protected function writeApnStream($apnURL, $payload)
    method getApnStream (line 275) | protected function getApnStream($apnURL)
    method closeApnStream (line 301) | protected function closeApnStream($apnURL)
    method getStreamContext (line 316) | protected function getStreamContext()
    method createPayload (line 356) | protected function createPayload($messageId, $expiry, $token, $message)
    method createMdmPayload (line 399) | public function createMdmPayload($token, $magicPushToken)
    method getResponses (line 413) | public function getResponses()
    method setPemAsString (line 422) | public function setPemAsString($pemContent, $passphrase) {
    method onKernelTerminate (line 437) | public function onKernelTerminate()
    method removeCachedPemFile (line 446) | private function removeCachedPemFile()
    method closeStreams (line 458) | private function closeStreams()

FILE: Service/OS/BlackberryNotification.php
  class BlackberryNotification (line 13) | class BlackberryNotification implements OSNotificationServiceInterface
    method __construct (line 59) | public function __construct($evaluation, $appID, $password, $timeout, ...
    method send (line 75) | public function send(MessageInterface $message)
    method doSend (line 90) | protected function doSend(BlackberryMessage $message)
    method constructMessageBody (line 120) | protected function constructMessageBody(BlackberryMessage $message, $s...
    method parseResponse (line 149) | protected function parseResponse(\Buzz\Message\Response $response)
    method getXMLBody (line 175) | private function getXMLBody(BlackberryMessage $message, $messageID)

FILE: Service/OS/MicrosoftNotification.php
  class MicrosoftNotification (line 12) | class MicrosoftNotification implements OSNotificationServiceInterface
    method __construct (line 32) | public function __construct($timeout, $logger)
    method send (line 40) | public function send(MessageInterface $message)

FILE: Service/OS/OSNotificationServiceInterface.php
  type OSNotificationServiceInterface (line 7) | interface OSNotificationServiceInterface
    method send (line 15) | public function send(MessageInterface $message);

FILE: Service/iOSFeedback.php
  class iOSFeedback (line 7) | class iOSFeedback
    method __construct (line 45) | public function __construct($sandbox, $pem, $passphrase, $timeout)
    method getDeviceUUIDs (line 60) | public function getDeviceUUIDs()
    method getStreamContext (line 101) | protected function getStreamContext()

FILE: Tests/DependencyInjection/ConfigurationTest.php
  class ConfigurationTest (line 8) | class ConfigurationTest extends \PHPUnit_Framework_TestCase
    method testDefaults (line 10) | public function testDefaults()
    method testAddingAndroidKeyRequiresValues (line 18) | public function testAddingAndroidKeyRequiresValues()
    method testAndroidRequiresUsername (line 29) | public function testAndroidRequiresUsername()
    method testAndroidRequiresPassword (line 42) | public function testAndroidRequiresPassword()
    method testOldFullAndroid (line 52) | public function testOldFullAndroid()
    method testNewC2DMIsAllowedWithoutOldBits (line 68) | public function testNewC2DMIsAllowedWithoutOldBits()
    method testGCMRequiresAPIKey (line 93) | public function testGCMRequiresAPIKey()
    method testGCMIsOK (line 106) | public function testGCMIsOK()
    method testAddingiOsKeyRequiresValues (line 131) | public function testAddingiOsKeyRequiresValues()
    method testiOSRequiresPEM (line 142) | public function testiOSRequiresPEM()
    method testFulliOS (line 152) | public function testFulliOS()
    method testAddingMacKeyRequiresValues (line 170) | public function testAddingMacKeyRequiresValues()
    method testMacRequiresPEM (line 181) | public function testMacRequiresPEM()
    method testFullMac (line 191) | public function testFullMac()
    method testBlackberryRequiresAppID (line 209) | public function testBlackberryRequiresAppID()
    method testBlackberryRequiresPassword (line 222) | public function testBlackberryRequiresPassword()
    method testFullBlackberry (line 232) | public function testFullBlackberry()
    method testAddingWindowsKeyRequiresValues (line 250) | public function testAddingWindowsKeyRequiresValues()
    method testFullWindows (line 260) | public function testFullWindows()
    method process (line 278) | protected function process($config)

FILE: Tests/Message/AndroidMessageTest.php
  class AndroidMessageTest (line 9) | class AndroidMessageTest extends \PHPUnit_Framework_TestCase
    method testCreation (line 11) | public function testCreation()
    method testCoreBodyGeneratedOK (line 18) | public function testCoreBodyGeneratedOK()
    method testMessageAddedOK (line 29) | public function testMessageAddedOK()
    method testNewCollapseKey (line 41) | public function testNewCollapseKey()
    method testRegistrationIDAddedToBody (line 53) | public function testRegistrationIDAddedToBody()
    method testCustomData (line 65) | public function testCustomData()
    method testTypeChangesBasedOnGCM (line 78) | public function testTypeChangesBasedOnGCM()
    method testSetIdentifierIsSingleEntryInGCMArray (line 86) | public function testSetIdentifierIsSingleEntryInGCMArray()
    method testAddingGCMIdentifiers (line 93) | public function testAddingGCMIdentifiers()
    method testSetMessageIsReturnedInGetData (line 101) | public function testSetMessageIsReturnedInGetData()

FILE: Tests/Message/BlackberryMessageTest.php
  class BlackberryMessageTest (line 9) | class BlackberryMessageTest extends \PHPUnit_Framework_TestCase
    method testCreation (line 11) | public function testCreation()
    method testDefaultBody (line 18) | public function testDefaultBody()
    method testSettingBody (line 25) | public function testSettingBody()

FILE: Tests/Message/MacMessageTest.php
  class MacMessageTest (line 9) | class MacMessageTest extends \PHPUnit_Framework_TestCase
    method testCreation (line 11) | public function testCreation()

FILE: Tests/Message/WindowsphoneMessageTest.php
  class WindowsphoneMessageTest (line 9) | class WindowsphoneMessageTest extends \PHPUnit_Framework_TestCase
    method testCreation (line 11) | public function testCreation()
    method testDefaultBody (line 18) | public function testDefaultBody()
    method testSettingBody (line 25) | public function testSettingBody()
    method testDefaultTarget (line 34) | public function testDefaultTarget()

FILE: Tests/Message/iOSMessageTest.php
  class iOSMessageTest (line 9) | class iOSMessageTest extends \PHPUnit_Framework_TestCase
    method testCreation (line 11) | public function testCreation()
    method testCoreBodyGeneratedOK (line 18) | public function testCoreBodyGeneratedOK()
    method testAPSAlertAddedOK (line 27) | public function testAPSAlertAddedOK()
    method testAPSBadgeAddedOK (line 39) | public function testAPSBadgeAddedOK()
    method testAPSSoundAddedOK (line 51) | public function testAPSSoundAddedOK()
    method testCustomDataAddedOK (line 63) | public function testCustomDataAddedOK()
    method testMutableContentAddOk (line 74) | public function testMutableContentAddOk()
Condensed preview — 46 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (116K chars).
[
  {
    "path": ".gitignore",
    "chars": 6,
    "preview": "vendor"
  },
  {
    "path": ".travis.yml",
    "chars": 554,
    "preview": "language: php\n\nbefore_install:\n  # If PHP >= 5.6, download & install PHPunit 5.7 to avoid builds failures\n  - if php -r "
  },
  {
    "path": "Command/TestPushCommand.php",
    "chars": 4289,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Command;\n\nuse Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand;"
  },
  {
    "path": "DependencyInjection/Compiler/AddHandlerPass.php",
    "chars": 2744,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\DependencyInjection\\Compiler;\n\nuse Symfony\\Component\\DependencyInjection\\Co"
  },
  {
    "path": "DependencyInjection/Configuration.php",
    "chars": 4837,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\DependencyInjection;\n\nuse Symfony\\Component\\Config\\Definition\\Builder\\TreeB"
  },
  {
    "path": "DependencyInjection/RMSPushNotificationsExtension.php",
    "chars": 7799,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\DependencyInjection;\n\nuse Symfony\\Component\\HttpKernel\\DependencyInjection\\"
  },
  {
    "path": "Device/Types.php",
    "chars": 520,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Device;\n\nclass Types\n{\n    const OS_ANDROID_C2DM = \"rms_push_notifications."
  },
  {
    "path": "Device/iOS/Feedback.php",
    "chars": 572,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Device\\iOS;\n\nclass Feedback\n{\n    public $timestamp;\n    public $tokenLengt"
  },
  {
    "path": "Exception/InvalidMessageTypeException.php",
    "chars": 121,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Exception;\n\nclass InvalidMessageTypeException extends \\RuntimeException\n{\n}"
  },
  {
    "path": "LICENSE",
    "chars": 1058,
    "preview": "Copyright (c) 2012-2015 Rich Sage\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this "
  },
  {
    "path": "Message/AndroidMessage.php",
    "chars": 4683,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types;\n\nclass AndroidMessa"
  },
  {
    "path": "Message/AppleMessage.php",
    "chars": 6592,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Message;\n\nclass AppleMessage implements MessageInterface\n{\n    /**\n     * C"
  },
  {
    "path": "Message/BlackberryMessage.php",
    "chars": 1592,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types;\n\nclass BlackberryMe"
  },
  {
    "path": "Message/MacMessage.php",
    "chars": 311,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types;\n\nclass MacMessage e"
  },
  {
    "path": "Message/MessageInterface.php",
    "chars": 339,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Message;\n\ninterface MessageInterface\n{\n    public function setMessage($mess"
  },
  {
    "path": "Message/WindowsphoneMessage.php",
    "chars": 1274,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types;\n\nclass Windowsphone"
  },
  {
    "path": "Message/iOSMessage.php",
    "chars": 311,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types;\n\nclass iOSMessage e"
  },
  {
    "path": "README.md",
    "chars": 5169,
    "preview": "# RMSPushNotificationsBundle ![](https://secure.travis-ci.org/richsage/RMSPushNotificationsBundle.png)\n\nA bundle to allo"
  },
  {
    "path": "RMSPushNotificationsBundle.php",
    "chars": 411,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle;\n\nuse Symfony\\Component\\HttpKernel\\Bundle\\Bundle;\nuse Symfony\\Component\\Dep"
  },
  {
    "path": "Resources/config/android.xml",
    "chars": 1902,
    "preview": "<?xml version=\"1.0\" ?>\n<container xmlns=\"http://symfony.com/schema/dic/services\"\n    xmlns:xsi=\"http://www.w3.org/2001/X"
  },
  {
    "path": "Resources/config/blackberry.xml",
    "chars": 1119,
    "preview": "<?xml version=\"1.0\" ?>\n<container xmlns=\"http://symfony.com/schema/dic/services\"\n    xmlns:xsi=\"http://www.w3.org/2001/X"
  },
  {
    "path": "Resources/config/ios.xml",
    "chars": 1716,
    "preview": "<?xml version=\"1.0\" ?>\n<container xmlns=\"http://symfony.com/schema/dic/services\"\n    xmlns:xsi=\"http://www.w3.org/2001/X"
  },
  {
    "path": "Resources/config/mac.xml",
    "chars": 1265,
    "preview": "<?xml version=\"1.0\" ?>\n<container xmlns=\"http://symfony.com/schema/dic/services\"\n    xmlns:xsi=\"http://www.w3.org/2001/X"
  },
  {
    "path": "Resources/config/services.xml",
    "chars": 1487,
    "preview": "<?xml version=\"1.0\" ?>\n<container xmlns=\"http://symfony.com/schema/dic/services\"\n    xmlns:xsi=\"http://www.w3.org/2001/X"
  },
  {
    "path": "Resources/config/windowsphone.xml",
    "chars": 897,
    "preview": "<?xml version=\"1.0\" ?>\n<container xmlns=\"http://symfony.com/schema/dic/services\"\n    xmlns:xsi=\"http://www.w3.org/2001/X"
  },
  {
    "path": "Service/EventListener.php",
    "chars": 567,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Service;\n\n\nclass EventListener {\n\n    /**\n     * @var EventListenerInterfac"
  },
  {
    "path": "Service/EventListenerInterface.php",
    "chars": 134,
    "preview": "<?php\nnamespace RMS\\PushNotificationsBundle\\Service;\n\n\ninterface EventListenerInterface {\n\n    public function onKernelT"
  },
  {
    "path": "Service/Notifications.php",
    "chars": 2644,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Service;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types;\nuse RMS\\PushNotific"
  },
  {
    "path": "Service/OS/AndroidGCMNotification.php",
    "chars": 4884,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Service\\OS;\n\nuse Psr\\Log\\LoggerInterface;\nuse RMS\\PushNotificationsBundle\\E"
  },
  {
    "path": "Service/OS/AndroidNotification.php",
    "chars": 3232,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Service\\OS;\n\nuse RMS\\PushNotificationsBundle\\Exception\\InvalidMessageTypeEx"
  },
  {
    "path": "Service/OS/AppleNotification.php",
    "chars": 13607,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Service\\OS;\n\nuse Psr\\Log\\LoggerInterface;\nuse RMS\\PushNotificationsBundle\\E"
  },
  {
    "path": "Service/OS/BlackberryNotification.php",
    "chars": 6200,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Service\\OS;\n\nuse Psr\\Log\\LoggerInterface;\nuse RMS\\PushNotificationsBundle\\E"
  },
  {
    "path": "Service/OS/MicrosoftNotification.php",
    "chars": 2177,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Service\\OS;\n\nuse Psr\\Log\\LoggerInterface;\nuse RMS\\PushNotificationsBundle\\E"
  },
  {
    "path": "Service/OS/OSNotificationServiceInterface.php",
    "chars": 372,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Service\\OS;\n\nuse RMS\\PushNotificationsBundle\\Message\\MessageInterface;\n\nint"
  },
  {
    "path": "Service/iOSFeedback.php",
    "chars": 2562,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Service;\n\nuse RMS\\PushNotificationsBundle\\Device\\iOS\\Feedback;\n\nclass iOSFe"
  },
  {
    "path": "Tests/DependencyInjection/ConfigurationTest.php",
    "chars": 8552,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Tests\\DependencyInjection;\n\nuse RMS\\PushNotificationsBundle\\DependencyInjec"
  },
  {
    "path": "Tests/Message/AndroidMessageTest.php",
    "chars": 3699,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Tests\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types,\n    RMS\\PushN"
  },
  {
    "path": "Tests/Message/BlackberryMessageTest.php",
    "chars": 943,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Tests\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types,\n    RMS\\PushN"
  },
  {
    "path": "Tests/Message/MacMessageTest.php",
    "chars": 519,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Tests\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types,\n    RMS\\PushN"
  },
  {
    "path": "Tests/Message/WindowsphoneMessageTest.php",
    "chars": 1213,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Tests\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types,\n    RMS\\PushN"
  },
  {
    "path": "Tests/Message/iOSMessageTest.php",
    "chars": 2296,
    "preview": "<?php\n\nnamespace RMS\\PushNotificationsBundle\\Tests\\Message;\n\nuse RMS\\PushNotificationsBundle\\Device\\Types,\n    RMS\\PushN"
  },
  {
    "path": "Tests/autoload.php.dist",
    "chars": 250,
    "preview": "<?php\n\n$vendorDir = __DIR__ . '/../vendor';\n \nif (!@include($vendorDir . '/autoload.php')) {\n    die(\"You must set up th"
  },
  {
    "path": "Tests/bootstrap.php",
    "chars": 172,
    "preview": "<?php\n\nif (file_exists($file = __DIR__.'/autoload.php')) {\n    require_once $file;\n} elseif (file_exists($file = __DIR__"
  },
  {
    "path": "composer.json",
    "chars": 1025,
    "preview": "{\n    \"name\": \"richsage/rms-push-notifications-bundle\",\n    \"type\": \"symfony-bundle\",\n    \"description\": \"Push notificat"
  },
  {
    "path": "phpunit.travis.xml",
    "chars": 575,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit backupGlobals=\"false\"\n         backupStaticAttributes=\"false\"\n         "
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 604,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n\r\n<phpunit backupGlobals=\"false\"\r\n         backupStaticAttributes=\"false\"\r\n     "
  }
]

About this extraction

This page contains the full source code of the richsage/RMSPushNotificationsBundle GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 46 files (105.3 KB), approximately 26.1k tokens, and a symbol index with 199 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!