diff --git a/composer.json b/composer.json index 9f12640..2f42660 100644 --- a/composer.json +++ b/composer.json @@ -25,19 +25,23 @@ }, "autoload-dev": { "psr-4": { - "Tests\\": "tests/" + "Welp\\MailchimpBundle\\Tests\\": "tests/" } }, "require": { - "php": ">=5.6", - "drewm/mailchimp-api": "2.5.*" + "php": ">=8.1", + "drewm/mailchimp-api": "^2.5", + "symfony/event-dispatcher": "^6.3||^7.0", + "symfony/framework-bundle": "^6.3||^7.0" }, "require-dev": { - "phpspec/phpspec": "~2@dev", - "phpunit/phpunit": "~5", - "symfony/symfony": ">=2.7" + "phpspec/phpspec": "^7.5", + "phpunit/phpunit": "^9.6", + "symfony/symfony": "^6.3||^7.0" }, "config": { - "bin-dir": "bin" + "bin-dir": "bin", + "preferred-install": "dist", + "sort-packages": true } } diff --git a/config/routes.yaml b/config/routes.yaml new file mode 100644 index 0000000..514d848 --- /dev/null +++ b/config/routes.yaml @@ -0,0 +1,3 @@ +welp_mailchimp_webhook: + resource: '../src/Controller' + type: attribute \ No newline at end of file diff --git a/src/Resources/config/services.yml b/config/services.yaml similarity index 95% rename from src/Resources/config/services.yml rename to config/services.yaml index 7aac2bc..d9c9959 100644 --- a/src/Resources/config/services.yml +++ b/config/services.yaml @@ -6,6 +6,13 @@ parameters: welp_mailchimp.config_list_provider_class: Welp\MailchimpBundle\Provider\ConfigListProvider services: + _defaults: + autowire: true + autoconfigure: true + + Welp\MailchimpBundle\Controller\: + resource: '../src/Controller/*' + # MailChimp welp_mailchimp.mailchimp_master: class: DrewM\MailChimp\MailChimp diff --git a/src/Command/SynchronizeMergeFieldsCommand.php b/src/Command/SynchronizeMergeFieldsCommand.php index 4c80468..fe79ca5 100644 --- a/src/Command/SynchronizeMergeFieldsCommand.php +++ b/src/Command/SynchronizeMergeFieldsCommand.php @@ -15,14 +15,14 @@ class SynchronizeMergeFieldsCommand extends Command * * @var ListSynchronizer */ - private $listSynchronizer; + private ListSynchronizer $listSynchronizer; /** * The configured list provider. * * @var ListProviderInterface */ - private $listProvider; + private ListProviderInterface $listProvider; public function __construct(ListSynchronizer $listSynchronizer, ListProviderInterface $listProvider) { @@ -32,7 +32,7 @@ public function __construct(ListSynchronizer $listSynchronizer, ListProviderInte parent::__construct(); } - protected function configure() + protected function configure(): void { $this ->setDescription('Synchronizing merge fields in MailChimp') @@ -41,7 +41,7 @@ protected function configure() ; } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln(sprintf('%s', $this->getDescription())); @@ -50,5 +50,7 @@ protected function execute(InputInterface $input, OutputInterface $output) foreach ($lists as $list) { $this->listSynchronizer->synchronizeMergeFields($list->getListId(), $list->getMergeFields()); } + + return Command::SUCCESS; } } diff --git a/src/Command/SynchronizeSubscribersCommand.php b/src/Command/SynchronizeSubscribersCommand.php index a71c88b..822c931 100644 --- a/src/Command/SynchronizeSubscribersCommand.php +++ b/src/Command/SynchronizeSubscribersCommand.php @@ -17,21 +17,21 @@ class SynchronizeSubscribersCommand extends Command * * @var ListSynchronizer */ - private $listSynchronizer; + private ListSynchronizer $listSynchronizer; /** * The configured list provider. * * @var ListProviderInterface */ - private $listProvider; + private ListProviderInterface $listProvider; /** * Mailchimp API class. * * @var MailChimp */ - private $mailchimp; + private MailChimp $mailchimp; public function __construct(ListSynchronizer $listSynchronizer, ListProviderInterface $listProvider, MailChimp $mailchimp) { @@ -45,7 +45,7 @@ public function __construct(ListSynchronizer $listSynchronizer, ListProviderInte /** * {@inheritdoc} */ - protected function configure() + protected function configure(): void { $this ->setDescription('Synchronizing subscribers in MailChimp') @@ -63,7 +63,7 @@ protected function configure() /** * {@inheritdoc} */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln(sprintf('%s', $this->getDescription())); @@ -72,16 +72,21 @@ protected function execute(InputInterface $input, OutputInterface $output) foreach ($lists as $list) { $output->writeln(sprintf('Synchronize list %s', $list->getListId())); $batchesResult = $this->listSynchronizer->synchronize($list); + if ($input->getOption('follow-sync')) { while (!$this->batchesFinished($batchesResult)) { $batchesResult = $this->refreshBatchesResult($batchesResult); + foreach ($batchesResult as $key => $batch) { $output->writeln($this->displayBatchInfo($batch)); } + sleep(2); } } } + + return Command::SUCCESS; } /** @@ -91,13 +96,13 @@ protected function execute(InputInterface $input, OutputInterface $output) * * @return array */ - private function refreshBatchesResult($batchesResult) + private function refreshBatchesResult(array $batchesResult): array { $refreshedBatchsResults = []; foreach ($batchesResult as $key => $batch) { $batch = $this->mailchimp->get('batches/'.$batch['id']); - array_push($refreshedBatchsResults, $batch); + $refreshedBatchsResults[] = $batch; } return $refreshedBatchsResults; @@ -110,11 +115,11 @@ private function refreshBatchesResult($batchesResult) * * @return bool */ - private function batchesFinished($batchesResult) + private function batchesFinished(array $batchesResult): bool { $allfinished = true; foreach ($batchesResult as $key => $batch) { - if ('finished' != $batch['status']) { + if ('finished' !== $batch['status']) { $allfinished = false; } } @@ -129,9 +134,9 @@ private function batchesFinished($batchesResult) * * @return string */ - private function displayBatchInfo($batch) + private function displayBatchInfo(array $batch): string { - if ('finished' == $batch['status']) { + if ('finished' === $batch['status']) { return sprintf('batch %s is finished, operations %d/%d with %d errors. http responses: %s', $batch['id'], $batch['finished_operations'], $batch['total_operations'], $batch['errored_operations'], $batch['response_body_url']); } diff --git a/src/Command/WebhookCommand.php b/src/Command/WebhookCommand.php index 7b42e16..59a677a 100644 --- a/src/Command/WebhookCommand.php +++ b/src/Command/WebhookCommand.php @@ -15,14 +15,14 @@ class WebhookCommand extends Command * * @var ListProviderInterface */ - private $listProvider; + private ListProviderInterface $listProvider; /** * The configured list repository. * * @var ListRepository */ - private $listRepository; + private ListRepository $listRepository; public function __construct(ListProviderInterface $listProvider, ListRepository $listRepository) { @@ -32,7 +32,7 @@ public function __construct(ListProviderInterface $listProvider, ListRepository parent::__construct(); } - protected function configure() + protected function configure(): void { $this ->setDescription('Add main webhook to a MailChimp List') @@ -41,7 +41,7 @@ protected function configure() // @TODO add params : listId, webhookurl } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln(sprintf('%s', $this->getDescription())); @@ -56,5 +56,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } $output->writeln('✔ done'); + + return Command::SUCCESS; } } diff --git a/src/Controller/WebhookController.php b/src/Controller/WebhookController.php index a0e2809..b859ad1 100644 --- a/src/Controller/WebhookController.php +++ b/src/Controller/WebhookController.php @@ -2,34 +2,33 @@ namespace Welp\MailchimpBundle\Controller; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; - +use Symfony\Component\Routing\Annotation\Route; // @TODO: deprecated since Symfony 6.4/7.0 use Welp\MailchimpBundle\Provider\ListProviderInterface; use Welp\MailchimpBundle\Event\WebhookEvent; -/** - * @Route("/webhook") - */ -class WebhookController extends Controller +#[Route('/webhook')] +class WebhookController extends AbstractController { /** * Endpoint for the mailchimp list Webhook * https://apidocs.mailchimp.com/webhooks/ - * @Route("/endpoint", name="webhook_index") - * @Method({"POST", "GET"}) * @param Request $request - * @throws AccessDeniedHttpException + * @param EventDispatcherInterface $eventDispatcher * @return JsonResponse */ - public function indexAction(Request $request) + #[Route('/endpoint', name: 'webhook_index')] + public function indexAction( + Request $request, + EventDispatcherInterface $eventDispatcher, + #[Autowire('@welp_mailchimp.list_provider')] $listProvider, + ): JsonResponse { - // For Mailchimp ping GET if ($request->isMethod('GET')) { return new JsonResponse([ @@ -37,10 +36,9 @@ public function indexAction(Request $request) 'ping' => 'pong', ]); } - // Handle POST request of Mailchimp $type = $request->request->get('type'); - $data = $request->request->get('data'); + $data = $request->request->all('data'); // all() returns an array /* Response example: data[merges][FNAME]: Tztz data[merges][EMAIL]: tztz@gmail.com @@ -55,64 +53,39 @@ public function indexAction(Request $request) $hooksecret = $request->query->get('hooksecret'); if (empty($type) || empty($data) || empty($hooksecret) || !array_key_exists('list_id', $data)) { - throw new AccessDeniedHttpException('incorrect data format!'); + throw $this->createAccessDeniedException('Incorrect data format!'); } $listId = $data['list_id']; - $listProviderKey = $this->getParameter('welp_mailchimp.list_provider'); - try { - $listProvider = $this->get($listProviderKey); - } catch (ServiceNotFoundException $e) { - throw new \InvalidArgumentException(sprintf('List Provider "%s" should be defined as a service.', $listProviderKey), $e->getCode(), $e); - } - if (!$listProvider instanceof ListProviderInterface) { - throw new \InvalidArgumentException(sprintf('List Provider "%s" should implement Welp\MailchimpBundle\Provider\ListProviderInterface.', $listProviderKey)); + throw new \InvalidArgumentException(sprintf('List Provider "%s" should implement Welp\MailchimpBundle\Provider\ListProviderInterface.', $listProvider::class)); } $list = $listProvider->getList($listId); // Check the webhook_secret $authorized = false; - if($list != null && $list->getWebhookSecret() == $hooksecret) { + + if ($list !== null && $list->getWebhookSecret() === $hooksecret) { $authorized = true; } if (!$authorized) { - throw new AccessDeniedHttpException('Webhook secret mismatch!'); + throw $this->createAccessDeniedException('Webhook secret mismatch!'); } - // Trigger the right event - switch ($type) { - case 'subscribe': - $dispatcher = $this->get('event_dispatcher'); - $dispatcher->dispatch(WebhookEvent::EVENT_SUBSCRIBE, new WebhookEvent($data)); - break; - case 'unsubscribe': - $dispatcher = $this->get('event_dispatcher'); - $dispatcher->dispatch(WebhookEvent::EVENT_UNSUBSCRIBE, new WebhookEvent($data)); - break; - case 'profile': - $dispatcher = $this->get('event_dispatcher'); - $dispatcher->dispatch(WebhookEvent::EVENT_PROFILE, new WebhookEvent($data)); - break; - case 'cleaned': - $dispatcher = $this->get('event_dispatcher'); - $dispatcher->dispatch(WebhookEvent::EVENT_CLEANED, new WebhookEvent($data)); - break; - case 'upemail': - $dispatcher = $this->get('event_dispatcher'); - $dispatcher->dispatch(WebhookEvent::EVENT_UPEMAIL, new WebhookEvent($data)); - break; - case 'campaign': - $dispatcher = $this->get('event_dispatcher'); - $dispatcher->dispatch(WebhookEvent::EVENT_CAMPAIGN, new WebhookEvent($data)); - break; - default: - throw new AccessDeniedHttpException('type mismatch!'); - break; - } + $eventName = match ($type) { + 'subscribe' => WebhookEvent::EVENT_SUBSCRIBE, + 'unsubscribe' => WebhookEvent::EVENT_UNSUBSCRIBE, + 'profile' => WebhookEvent::EVENT_PROFILE, + 'cleaned' => WebhookEvent::EVENT_CLEANED, + 'upemail' => WebhookEvent::EVENT_UPEMAIL, + 'campaign' => WebhookEvent::EVENT_CAMPAIGN, + default => throw $this->createAccessDeniedException('Type mismatch!'), + }; + + $eventDispatcher->dispatch(new WebhookEvent($data), $eventName); return new JsonResponse([ 'type' => $type, diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 0cc8a99..3e9bc4e 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -15,17 +15,12 @@ class Configuration implements ConfigurationInterface /** * {@inheritDoc} */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('welp_mailchimp'); - if (method_exists($treeBuilder, 'getRootNode')) { - $rootNode = $treeBuilder->getRootNode(); - } else { - $rootNode = $treeBuilder->root('welp_mailchimp'); - } - - $rootNode + $treeBuilder + ->getRootNode() ->children() ->scalarNode('api_key') ->isRequired() diff --git a/src/DependencyInjection/WelpMailchimpExtension.php b/src/DependencyInjection/WelpMailchimpExtension.php index 72c3a2e..69081ff 100644 --- a/src/DependencyInjection/WelpMailchimpExtension.php +++ b/src/DependencyInjection/WelpMailchimpExtension.php @@ -4,13 +4,13 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; class WelpMailchimpExtension extends Extension { - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); @@ -19,8 +19,8 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('welp_mailchimp.list_provider', $config['list_provider']); $container->setParameter('welp_mailchimp.api_key', isset($config['api_key']) ? $config['api_key'] : null); - $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('services.yml'); + $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../../config')); + $loader->load('services.yaml'); // create an alias for the chosen list provider service $alias = $config['list_provider']; @@ -35,9 +35,10 @@ public function getAlias(): string return 'welp_mailchimp'; } - public function loadSubscriberProviders(ContainerBuilder $container, $lists) + public function loadSubscriberProviders(ContainerBuilder $container, $lists): void { $providerFactory = $container->getDefinition('welp_mailchimp.provider.factory'); + foreach ($lists as $list) { $providerKey = $list['subscriber_provider']; $providerFactory->addMethodCall('addProvider', [$providerKey, new Reference($providerKey)]); diff --git a/src/Event/SubscriberEvent.php b/src/Event/SubscriberEvent.php index 68934f4..5587347 100644 --- a/src/Event/SubscriberEvent.php +++ b/src/Event/SubscriberEvent.php @@ -2,7 +2,7 @@ namespace Welp\MailchimpBundle\Event; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; use Welp\MailchimpBundle\Subscriber\Subscriber; /** @@ -14,69 +14,69 @@ class SubscriberEvent extends Event * Event to subscribe a User * @var string */ - const EVENT_SUBSCRIBE = 'welp.mailchimp.subscribe'; + public const EVENT_SUBSCRIBE = 'welp.mailchimp.subscribe'; /** * Event to unsubscribe a User * @var string */ - const EVENT_UNSUBSCRIBE = 'welp.mailchimp.unsubscribe'; + public const EVENT_UNSUBSCRIBE = 'welp.mailchimp.unsubscribe'; /** * Event to pending a User * @var string */ - const EVENT_PENDING = 'welp.mailchimp.pending'; + public const EVENT_PENDING = 'welp.mailchimp.pending'; /** * Event to clean a User * @var string */ - const EVENT_CLEAN = 'welp.mailchimp.clean'; + public const EVENT_CLEAN = 'welp.mailchimp.clean'; /** * Event to update a User * @var string */ - const EVENT_UPDATE = 'welp.mailchimp.update'; + public const EVENT_UPDATE = 'welp.mailchimp.update'; /** * Event to change email of a User * @var string */ - const EVENT_CHANGE_EMAIL = 'welp.mailchimp.change_email'; + public const EVENT_CHANGE_EMAIL = 'welp.mailchimp.change_email'; /** * Event to delete a User * @var string */ - const EVENT_DELETE = 'welp.mailchimp.delete'; + public const EVENT_DELETE = 'welp.mailchimp.delete'; /** * MailChimp ListId * @var string */ - protected $listId; + protected string $listId; /** * User as Subscriber * @var Subscriber */ - protected $subscriber; + protected Subscriber $subscriber; /** * Subscriber's oldEmail (used for EVENT_CHANGE_EMAIL) * @var string */ - protected $oldEmail; + protected ?string $oldEmail; /** * - * @param string $listId + * @param string $listId * @param Subscriber $subscriber - * @param string $oldEmail + * @param string|null $oldEmail */ - public function __construct($listId, Subscriber $subscriber, $oldEmail = null) + public function __construct(string $listId, Subscriber $subscriber, ?string $oldEmail = null) { $this->listId = $listId; $this->subscriber = $subscriber; @@ -87,7 +87,7 @@ public function __construct($listId, Subscriber $subscriber, $oldEmail = null) * Get MailChimp listId * @return string */ - public function getListId() + public function getListId(): string { return $this->listId; } @@ -96,16 +96,16 @@ public function getListId() * Get the User as Subscriber * @return Subscriber */ - public function getSubscriber() + public function getSubscriber(): Subscriber { return $this->subscriber; } /** * Get Subscriber's oldEmail (used for EVENT_CHANGE_EMAIL) - * @return string + * @return string|null */ - public function getOldEmail() + public function getOldEmail(): ?string { return $this->oldEmail; } diff --git a/src/Event/SubscriberListener.php b/src/Event/SubscriberListener.php index c6c8663..bb5a380 100644 --- a/src/Event/SubscriberListener.php +++ b/src/Event/SubscriberListener.php @@ -2,18 +2,18 @@ namespace Welp\MailchimpBundle\Event; +use Symfony\Contracts\EventDispatcher\Event; use Welp\MailchimpBundle\Subscriber\ListRepository; -use Welp\MailchimpBundle\Event\SubscriberEvent; /** * Listener for subscriber unit synchronization */ -class SubscriberListener +class SubscriberListener extends Event { /** * @var ListRepository */ - protected $listRepository; + protected ListRepository $listRepository; /** * @param ListRepository $listRepository @@ -28,7 +28,7 @@ public function __construct(ListRepository $listRepository) * @param SubscriberEvent $event * @return void */ - public function onSubscribe(SubscriberEvent $event) + public function onSubscribe(SubscriberEvent $event): void { $this->listRepository->subscribe( $event->getListId(), @@ -41,7 +41,7 @@ public function onSubscribe(SubscriberEvent $event) * @param SubscriberEvent $event * @return void */ - public function onUnsubscribe(SubscriberEvent $event) + public function onUnsubscribe(SubscriberEvent $event): void { $this->listRepository->unsubscribe( $event->getListId(), @@ -54,7 +54,7 @@ public function onUnsubscribe(SubscriberEvent $event) * @param SubscriberEvent $event * @return void */ - public function onPending(SubscriberEvent $event) + public function onPending(SubscriberEvent $event): void { $this->listRepository->pending( $event->getListId(), @@ -67,7 +67,7 @@ public function onPending(SubscriberEvent $event) * @param SubscriberEvent $event * @return void */ - public function onClean(SubscriberEvent $event) + public function onClean(SubscriberEvent $event): void { $this->listRepository->clean( $event->getListId(), @@ -80,7 +80,7 @@ public function onClean(SubscriberEvent $event) * @param SubscriberEvent $event * @return void */ - public function onUpdate(SubscriberEvent $event) + public function onUpdate(SubscriberEvent $event): void { $this->listRepository->update( $event->getListId(), @@ -93,7 +93,7 @@ public function onUpdate(SubscriberEvent $event) * @param SubscriberEvent $event * @return void */ - public function onChangeEmail(SubscriberEvent $event) + public function onChangeEmail(SubscriberEvent $event): void { $this->listRepository->changeEmailAddress( $event->getListId(), @@ -107,7 +107,7 @@ public function onChangeEmail(SubscriberEvent $event) * @param SubscriberEvent $event * @return void */ - public function onDelete(SubscriberEvent $event) + public function onDelete(SubscriberEvent $event): void { $this->listRepository->delete( $event->getListId(), diff --git a/src/Event/WebhookEvent.php b/src/Event/WebhookEvent.php index 8f808d5..a75cca2 100644 --- a/src/Event/WebhookEvent.php +++ b/src/Event/WebhookEvent.php @@ -2,7 +2,7 @@ namespace Welp\MailchimpBundle\Event; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * Event for MailChimp webhook @@ -13,44 +13,44 @@ class WebhookEvent extends Event * Event triggered when received webhook for user subscribe * @var string */ - const EVENT_SUBSCRIBE = 'welp.mailchimp.webhook.subscribe'; + public const EVENT_SUBSCRIBE = 'welp.mailchimp.webhook.subscribe'; /** * Event triggered when received webhook for user unsubscribe * @var string */ - const EVENT_UNSUBSCRIBE = 'welp.mailchimp.webhook.unsubscribe'; + public const EVENT_UNSUBSCRIBE = 'welp.mailchimp.webhook.unsubscribe'; /** * Event triggered when received webhook for user update profile * @var string */ - const EVENT_PROFILE = 'welp.mailchimp.webhook.profile'; + public const EVENT_PROFILE = 'welp.mailchimp.webhook.profile'; /** * Event triggered when received webhook for user cleaned * @var string */ - const EVENT_CLEANED = 'welp.mailchimp.webhook.cleaned'; + public const EVENT_CLEANED = 'welp.mailchimp.webhook.cleaned'; /** * Event triggered when received webhook for user update email [legacy?] * @var string */ - const EVENT_UPEMAIL = 'welp.mailchimp.webhook.upemail'; + public const EVENT_UPEMAIL = 'welp.mailchimp.webhook.upemail'; /** * Event triggered when received webhook for new campaign send * @var string */ - const EVENT_CAMPAIGN = 'welp.mailchimp.webhook.campaign'; + public const EVENT_CAMPAIGN = 'welp.mailchimp.webhook.campaign'; /** * Data form webhook request * @var array */ - protected $data; + protected array $data; /** * * @param array $data */ - public function __construct($data) + public function __construct(array $data) { $this->data = $data; } @@ -59,7 +59,7 @@ public function __construct($data) * Get data form webhook request * @return array */ - public function getData() + public function getData(): array { return $this->data; } diff --git a/src/Exception/MailchimpException.php b/src/Exception/MailchimpException.php index c4a9416..b007310 100644 --- a/src/Exception/MailchimpException.php +++ b/src/Exception/MailchimpException.php @@ -12,20 +12,20 @@ class MailchimpException extends \Exception * for this occurrence of the problem. * @var int */ - private $status; + private int $status; /** * A human-readable explanation specific to this occurrence of the problem. * @var string */ - private $detail; + private string $detail; /** * An absolute URI that identifies the problem type. When dereferenced, * it should provide human-readable documentation for the problem type. * @var string */ - private $type; + private string $type; /** * A short, human-readable summary of the problem type. @@ -33,20 +33,20 @@ class MailchimpException extends \Exception * except for purposes of localization. * @var string */ - private $title; + private string $title; /** * For field-specific details, see the 'errors' array. - * @var array + * @var array|null */ - private $errors; + private ?array $errors; /** * A string that identifies this specific occurrence of the problem. * Please provide this ID when contacting support. * @var string */ - private $instance; + private ?string $instance; /** * http://developer.mailchimp.com/documentation/mailchimp/guides/get-started-with-mailchimp-api-3/#errors @@ -57,8 +57,9 @@ class MailchimpException extends \Exception * @param array $errors * @param string $instance * @param \Throwable $previous + * @todo Fix $status: Optional parameter is provided before required. */ - public function __construct($status=0, $detail, $type, $title, $errors=null, $instance=null, \Throwable $previous=null) + public function __construct($status = 0, $detail, $type, $title, $errors = null, $instance = null, \Throwable $previous=null) { parent::__construct($detail, $status, $previous); @@ -75,7 +76,7 @@ public function __construct($status=0, $detail, $type, $title, $errors=null, $in * * @return static */ - public function setType($type) + public function setType(string $type): static { $this->type = $type; return $this; @@ -86,7 +87,7 @@ public function setType($type) * * @return static */ - public function setTitle($title) + public function setTitle(string $title): static { $this->title = $title; return $this; @@ -97,7 +98,7 @@ public function setTitle($title) * * @return static */ - public function setErrors(array $errors) + public function setErrors(array $errors): static { $this->errors = $errors; return $this; @@ -108,7 +109,7 @@ public function setErrors(array $errors) * * @return static */ - public function setInstance($instance) + public function setInstance(string $instance): static { $this->instance = $instance; return $this; @@ -117,7 +118,7 @@ public function setInstance($instance) /** * @return int */ - public function getStatus() + public function getStatus(): int { return $this->status; } @@ -125,7 +126,7 @@ public function getStatus() /** * @return string */ - public function getDetail() + public function getDetail(): string { return $this->detail; } @@ -133,7 +134,7 @@ public function getDetail() /** * @return string */ - public function getType() + public function getType(): string { return $this->type; } @@ -141,7 +142,7 @@ public function getType() /** * @return string */ - public function getTitle() + public function getTitle(): string { return $this->title; } @@ -149,13 +150,14 @@ public function getTitle() /** * @return string */ - public function getInstance() + public function getInstance(): ?string { return $this->instance; } /** * @return + * @todo: Artifact? */ public function getString() { @@ -163,9 +165,9 @@ public function getString() } /** - * @return array + * @return array|null */ - public function getErrors() + public function getErrors(): ?array { return $this->errors; } diff --git a/src/Provider/ConfigListProvider.php b/src/Provider/ConfigListProvider.php index bc39e41..9642259 100644 --- a/src/Provider/ConfigListProvider.php +++ b/src/Provider/ConfigListProvider.php @@ -2,13 +2,13 @@ namespace Welp\MailchimpBundle\Provider; -use Welp\MailchimpBundle\Provider\ListProviderInterface; use Welp\MailchimpBundle\Subscriber\SubscriberList; +use Welp\MailchimpBundle\Subscriber\SubscriberListInterface; class ConfigListProvider implements ListProviderInterface { - private $providerFactory; - private $lists; + private ProviderFactory $providerFactory; + private array $lists; public function __construct(ProviderFactory $providerFactory, $lists) { @@ -20,16 +20,18 @@ public function __construct(ProviderFactory $providerFactory, $lists) * Default list provider, retrieve the lists from the config file. * {@inheritdoc} */ - public function getLists() + public function getLists(): array { - if (sizeof($this->lists) == 0) { - throw new \RuntimeException("No Mailchimp list has been defined. Check the your config.yml file based on MailchimpBundle's README.md"); + if (count($this->lists) === 0) { + throw new \RuntimeException('No Mailchimp list has been defined. Check the your config.yml file based on MailchimpBundle\'s README.md'); } - $lists = array(); - foreach ($this->lists as $listId => $listParameters) - { + + $lists = []; + + foreach ($this->lists as $listId => $listParameters) { $lists[] = $this->createList($listId, $listParameters); } + return $lists; } @@ -37,24 +39,24 @@ public function getLists() * Default list provider, retrieve one list from the config file. * {@inheritdoc} */ - public function getList($listId) + public function getList($listId): SubscriberListInterface { - foreach ($this->lists as $id => $listParameters) - { - if($id == $listId) - { + foreach ($this->lists as $id => $listParameters) { + if ($id === $listId) { return $this->createList($id, $listParameters); } } + + throw new \RuntimeException('Selected Mailchimp list has not been found. Check the your config.yml file based on MailchimpBundle\'s README.md'); } /** * create one SubscriberList * @param string $listId - * @param string $listParameters + * @param array $listParameters * @return SubscriberList */ - private function createList($listId, $listParameters) + private function createList(string $listId, array $listParameters): SubscriberList { $providerServiceKey = $listParameters['subscriber_provider']; $provider = $this->providerFactory->create($providerServiceKey); diff --git a/src/Provider/DynamicProviderInterface.php b/src/Provider/DynamicProviderInterface.php index 17d92a8..c5541ea 100644 --- a/src/Provider/DynamicProviderInterface.php +++ b/src/Provider/DynamicProviderInterface.php @@ -14,5 +14,5 @@ interface DynamicProviderInterface extends ProviderInterface * @param string $listId * @return void */ - public function setListId(string $listId); + public function setListId(string $listId): void; } diff --git a/src/Provider/ExampleSubscriberProvider.php b/src/Provider/ExampleSubscriberProvider.php index 1536b50..f49da3c 100644 --- a/src/Provider/ExampleSubscriberProvider.php +++ b/src/Provider/ExampleSubscriberProvider.php @@ -27,11 +27,11 @@ public function __construct(UserRepository $userRepository) /** * {@inheritdoc} */ - public function getSubscribers() + public function getSubscribers(): array { $users = $this->userRepository->findSubscribers(); - $subscribers = array_map(function (User $user) { + $subscribers = array_map(static function (User $user) { $subscriber = new Subscriber($user->getEmail(), [ self::TAG_NICKNAME => $user->getNickname(), self::TAG_GENDER => $user->getGender(), diff --git a/src/Provider/FosSubscriberProvider.php b/src/Provider/FosSubscriberProvider.php index f041656..0a34387 100644 --- a/src/Provider/FosSubscriberProvider.php +++ b/src/Provider/FosSubscriberProvider.php @@ -25,13 +25,13 @@ public function __construct(UserManagerInterface $userManager) /** * {@inheritdoc} */ - public function getSubscribers() + public function getSubscribers(): array { $users = $this->userManager->findUsers(); // or find only enabled users : // $users = $this->userManager->findUserBy(array('enabled' => true)); - $subscribers = array_map(function (User $user) { + $subscribers = array_map(static function (User $user) { $subscriber = new Subscriber($user->getEmail(), [ self::TAG_USERNAME => $user->getUsername(), self::TAG_ENABLED => $user->isEnabled(), diff --git a/src/Provider/ListProviderInterface.php b/src/Provider/ListProviderInterface.php index c80825d..98f8192 100644 --- a/src/Provider/ListProviderInterface.php +++ b/src/Provider/ListProviderInterface.php @@ -2,6 +2,8 @@ namespace Welp\MailchimpBundle\Provider; +use Welp\MailchimpBundle\Subscriber\SubscriberListInterface; + /** * List provider interface */ @@ -11,11 +13,12 @@ interface ListProviderInterface * Get all the available Mailchimp lists * @return array of SubscriberListInterface */ - public function getLists(); + public function getLists(): array; /** * Get one Mailchimp list by id + * @param $listId * @return SubscriberListInterface */ - public function getList($listId); + public function getList($listId): SubscriberListInterface; } diff --git a/src/Provider/ProviderFactory.php b/src/Provider/ProviderFactory.php index 65ace96..ccc730d 100644 --- a/src/Provider/ProviderFactory.php +++ b/src/Provider/ProviderFactory.php @@ -9,7 +9,7 @@ class ProviderFactory * * @var array */ - private $providers; + private array $providers; /** * Add a provider to the provider array. @@ -28,7 +28,7 @@ public function addProvider(string $providerKey, ProviderInterface $provider): v * * @return ProviderInterface $provider */ - public function create($providerKey) + public function create(string $providerKey): ProviderInterface { if (!isset($this->providers[$providerKey])) { throw new \InvalidArgumentException(sprintf('Provider "%s" should be defined as a service.', $providerKey)); diff --git a/src/Provider/ProviderInterface.php b/src/Provider/ProviderInterface.php index 1aee611..92a4a62 100644 --- a/src/Provider/ProviderInterface.php +++ b/src/Provider/ProviderInterface.php @@ -12,5 +12,5 @@ interface ProviderInterface * in order to sync with MailChimp List * @return array of Subscriber */ - public function getSubscribers(); + public function getSubscribers(): array; } diff --git a/src/Resources/config/routing.yml b/src/Resources/config/routing.yml deleted file mode 100644 index c4033b7..0000000 --- a/src/Resources/config/routing.yml +++ /dev/null @@ -1,3 +0,0 @@ -welp_mailchimp_webhook: - resource: Welp\MailchimpBundle\Controller\WebhookController - type: annotation diff --git a/src/Subscriber/ListRepository.php b/src/Subscriber/ListRepository.php index b43666d..6a03859 100644 --- a/src/Subscriber/ListRepository.php +++ b/src/Subscriber/ListRepository.php @@ -3,7 +3,6 @@ namespace Welp\MailchimpBundle\Subscriber; use DrewM\MailChimp\MailChimp; -use phpDocumentor\Reflection\Types\Void_; use Welp\MailchimpBundle\Exception\MailchimpException; /** @@ -15,19 +14,19 @@ class ListRepository * Numbers of subscribers per batch * @var int */ - const SUBSCRIBER_BATCH_SIZE = 300; + public const SUBSCRIBER_BATCH_SIZE = 300; /** * MailChimp count limit for result set * @var int */ - const MAILCHIMP_DEFAULT_COUNT_LIMIT = 10; + public const MAILCHIMP_DEFAULT_COUNT_LIMIT = 10; /** * MailChimp Object * @var MailChimp */ - protected $mailchimp; + protected MailChimp $mailchimp; /** * @param MailChimp $mailchimp @@ -41,23 +40,25 @@ public function __construct(MailChimp $mailchimp) * Get MailChimp Object to do custom actions * @return MailChimp https://github.com/drewm/mailchimp-api */ - public function getMailChimp() + public function getMailChimp(): MailChimp { return $this->mailchimp; } /** - * Find MailChimp List by list Id + * Find MailChimp List by list ID * @param string $listId - * @return array list http://developer.mailchimp.com/documentation/mailchimp/reference/lists/#read-get_lists_list_id + * @return array list https://mailchimp.com/developer/marketing/api/lists/get-list-info/ + * @throws MailchimpException */ - public function findById($listId) + public function findById(string $listId): array { - $listData = $this->mailchimp->get("lists/$listId"); + $listData = $this->mailchimp->get('lists/' . $listId); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); } + return $listData; } @@ -67,14 +68,17 @@ public function findById($listId) * @param Subscriber $subscriber * @param string $status * @return array + * @throws MailchimpException */ - protected function putSubscriberInList($listId, Subscriber $subscriber, $status) + protected function putSubscriberInList(string $listId, Subscriber $subscriber, string $status): array { if (!in_array($status, ['subscribed', 'unsubscribed', 'cleaned', 'pending', 'transactional'])) { throw new \RuntimeException('$status must be one of these values: subscribed, unsubscribed, cleaned, pending, transactional'); } + $subscriberHash = MailChimp::subscriberHash($subscriber->getEmail()); - $result = $this->mailchimp->put("lists/$listId/members/$subscriberHash", + + $result = $this->mailchimp->put('lists/' . $listId . '/members/' . $subscriberHash, array_merge( $subscriber->formatMailChimp(), ['status' => $status] @@ -93,8 +97,9 @@ protected function putSubscriberInList($listId, Subscriber $subscriber, $status) * @param string $listId * @param Subscriber $subscriber * @return array + * @throws MailchimpException */ - public function subscribe($listId, Subscriber $subscriber) + public function subscribe(string $listId, Subscriber $subscriber): array { return $this->putSubscriberInList($listId, $subscriber, 'subscribed'); } @@ -103,8 +108,9 @@ public function subscribe($listId, Subscriber $subscriber) * Subscribe a Subscriber to a list * @param string $listId * @param Subscriber $subscriber + * @throws MailchimpException */ - public function unsubscribe($listId, Subscriber $subscriber) + public function unsubscribe(string $listId, Subscriber $subscriber): array { return $this->putSubscriberInList($listId, $subscriber, 'unsubscribed'); } @@ -113,8 +119,10 @@ public function unsubscribe($listId, Subscriber $subscriber) * Clean a Subscriber to a list * @param string $listId * @param Subscriber $subscriber + * @return array + * @throws MailchimpException */ - public function clean($listId, Subscriber $subscriber) + public function clean(string $listId, Subscriber $subscriber): array { return $this->putSubscriberInList($listId, $subscriber, 'cleaned'); } @@ -123,8 +131,10 @@ public function clean($listId, Subscriber $subscriber) * Add/set pending a Subscriber to a list * @param string $listId * @param Subscriber $subscriber + * @return array + * @throws MailchimpException */ - public function pending($listId, Subscriber $subscriber) + public function pending(string $listId, Subscriber $subscriber): array { return $this->putSubscriberInList($listId, $subscriber, 'pending'); } @@ -133,22 +143,26 @@ public function pending($listId, Subscriber $subscriber) * set transactional a Subscriber to a list * @param string $listId * @param Subscriber $subscriber + * @return array + * @throws MailchimpException */ - public function transactional($listId, Subscriber $subscriber) + public function transactional(string $listId, Subscriber $subscriber): array { return $this->putSubscriberInList($listId, $subscriber, 'transactional'); } /** * Update a Subscriber to a list - * http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#edit-patch_lists_list_id_members_subscriber_hash + * https://mailchimp.com/developer/marketing/api/list-members/update-list-member/ * @param string $listId * @param Subscriber $subscriber + * @return array|bool + * @throws MailchimpException */ - public function update($listId, Subscriber $subscriber) + public function update(string $listId, Subscriber $subscriber): bool|array { $subscriberHash = MailChimp::subscriberHash($subscriber->getEmail()); - $result = $this->mailchimp->patch("lists/$listId/members/$subscriberHash", $subscriber->formatMailChimp()); + $result = $this->mailchimp->patch('lists/' . $listId . '/members/' . $subscriberHash, $subscriber->formatMailChimp()); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); @@ -162,20 +176,23 @@ public function update($listId, Subscriber $subscriber) * one possible solution is to delete old subscriber and add a new one * with the same mergeFieds and Options... * Change email address - * http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#edit-put_lists_list_id_members_subscriber_hash + * https://mailchimp.com/developer/marketing/api/list-members/update-list-member/ * @param string $listId * @param Subscriber $newSubscriber * @param string $oldEmailAddress + * @return array|bool + * @throws MailchimpException */ - public function changeEmailAddress($listId, Subscriber $newSubscriber, $oldEmailAddress) + public function changeEmailAddress(string $listId, Subscriber $newSubscriber, string $oldEmailAddress): bool|array { # @NOTE handle special cases: # 1. new email address already exists in List # 2. old email address not exists in list - # 3. old or new email address doesn't exists in list + # 3. old or new email address doesn't exist in list $subscriberHash = MailChimp::subscriberHash($oldEmailAddress); - $oldMember = $this->mailchimp->get("lists/$listId/members/$subscriberHash"); + $oldMember = $this->mailchimp->get('lists/' . $listId . '/members/' . $subscriberHash); + if (!$this->mailchimp->success()) { // problem with the oldSubcriber (doest not exist, ...) // np we will take the new Subscriber @@ -183,21 +200,15 @@ public function changeEmailAddress($listId, Subscriber $newSubscriber, $oldEmail $newMember['status_if_new'] = 'subscribed'; } else { // clean member - unset($oldMember['_links']); - unset($oldMember['id']); - unset($oldMember['stats']); - unset($oldMember['unique_email_id']); - unset($oldMember['member_rating']); - unset($oldMember['last_changed']); - unset($oldMember['email_client']); - unset($oldMember['list_id']); + unset($oldMember['_links'], $oldMember['id'], $oldMember['stats'], $oldMember['unique_email_id'], $oldMember['member_rating'], $oldMember['last_changed'], $oldMember['email_client'], $oldMember['list_id']); $newMember = $oldMember; $newMember['email_address'] = $newSubscriber->getEmail(); $newMember['status_if_new'] = 'subscribed'; // delete the old Member - $deleteOld = $this->mailchimp->delete("lists/$listId/members/$subscriberHash"); + $deleteOld = $this->mailchimp->delete('lists/' . $listId . '/members/' . $subscriberHash); + if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); } @@ -205,7 +216,8 @@ public function changeEmailAddress($listId, Subscriber $newSubscriber, $oldEmail // add/update the new member $subscriberHash = MailChimp::subscriberHash($newSubscriber->getEmail()); - $result = $this->mailchimp->put("lists/$listId/members/$subscriberHash", $newMember); + $result = $this->mailchimp->put('lists/' . $listId . '/members/' . $subscriberHash, $newMember); + if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); } @@ -217,11 +229,13 @@ public function changeEmailAddress($listId, Subscriber $newSubscriber, $oldEmail * Delete a Subscriber to a list * @param string $listId * @param Subscriber $subscriber + * @return array|bool + * @throws MailchimpException */ - public function delete($listId, Subscriber $subscriber) + public function delete(string $listId, Subscriber $subscriber): bool|array { $subscriberHash = MailChimp::subscriberHash($subscriber->getEmail()); - $result = $this->mailchimp->delete("lists/$listId/members/$subscriberHash"); + $result = $this->mailchimp->delete('lists/' . $listId . '/members/' . $subscriberHash); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); @@ -236,24 +250,32 @@ public function delete($listId, Subscriber $subscriber) * @param array $subscribers * @return array $batchIds */ - public function batchSubscribe($listId, array $subscribers) + public function batchSubscribe(string $listId, array $subscribers): array { $batchResults = []; // as suggested in MailChimp API docs, we send multiple smaller requests instead of a bigger one $subscriberChunks = array_chunk($subscribers, self::SUBSCRIBER_BATCH_SIZE); + foreach ($subscriberChunks as $subscriberChunk) { - $Batch = $this->mailchimp->new_batch(); + $batch = $this->mailchimp->new_batch(); + foreach ($subscriberChunk as $index => $newsubscribers) { $subscriberHash = MailChimp::subscriberHash($newsubscribers->getEmail()); - $Batch->put("op$index", "lists/$listId/members/$subscriberHash", array_merge( - $newsubscribers->formatMailChimp(), - ['status' => 'subscribed'] - )); + + $batch->put( + 'op' . $index, + 'lists/' . $listId . '/members/' . $subscriberHash, + array_merge( + $newsubscribers->formatMailChimp(), + ['status' => 'subscribed'] + ) + ); } - $Batch->execute(); - $currentBatch = $Batch->check_status(); - array_push($batchResults, $currentBatch); + $batch->execute(); + $currentBatch = $batch->check_status(); + $batchResults[] = $currentBatch; // faster than array_push() here } + return $batchResults; } @@ -263,23 +285,28 @@ public function batchSubscribe($listId, array $subscribers) * @param array $emails * @return array $batchIds */ - public function batchUnsubscribe($listId, array $emails) + public function batchUnsubscribe(string $listId, array $emails): array { $batchIds = []; // as suggested in MailChimp API docs, we send multiple smaller requests instead of a bigger one $emailsChunks = array_chunk($emails, self::SUBSCRIBER_BATCH_SIZE); + foreach ($emailsChunks as $emailsChunk) { - $Batch = $this->mailchimp->new_batch(); + $batch = $this->mailchimp->new_batch(); + foreach ($emailsChunk as $index => $email) { $emailHash = MailChimp::subscriberHash($email); - $Batch->patch("op$index", "lists/$listId/members/$emailHash", [ + $batch->patch('op' . $index, 'lists/' . $listId . '/members/' . $emailHash, [ 'status' => 'unsubscribed' ]); } - $result = $Batch->execute(); - $currentBatch = $Batch->check_status(); - array_push($batchIds, $currentBatch['id']); + + $batch->execute(); + $currentBatch = $batch->check_status(); + + $batchIds[] = $currentBatch['id']; // faster than array_push() here } + return $batchIds; } @@ -289,20 +316,23 @@ public function batchUnsubscribe($listId, array $emails) * @param array $emails * @return array $batchIds */ - public function batchDelete($listId, array $emails) + public function batchDelete(string $listId, array $emails): array { $batchIds = []; // as suggested in MailChimp API docs, we send multiple smaller requests instead of a bigger one $emailsChunks = array_chunk($emails, self::SUBSCRIBER_BATCH_SIZE); + foreach ($emailsChunks as $emailsChunk) { - $Batch = $this->mailchimp->new_batch(); + $batch = $this->mailchimp->new_batch(); + foreach ($emailsChunk as $index => $email) { $emailHash = MailChimp::subscriberHash($email); - $Batch->delete("op$index", "lists/$listId/members/$emailHash"); + $batch->delete('op' . $index, 'lists/' . $listId . '/members/' . $emailHash); } - $result = $Batch->execute(); - $currentBatch = $Batch->check_status(); - array_push($batchIds, $currentBatch['id']); + + $batch->execute(); + $currentBatch = $batch->check_status(); + $batchIds[] = $currentBatch['id']; } return $batchIds; } @@ -311,11 +341,11 @@ public function batchDelete($listId, array $emails) * Get Members of a list * @param string $listId * @return array + * @throws MailchimpException */ - public function getMembers($listId) + public function getMembers(string $listId): array { - $emails = []; - $result = $this->mailchimp->get("lists/$listId/members"); + $result = $this->mailchimp->get('lists/' . $listId . '/members'); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); @@ -328,14 +358,15 @@ public function getMembers($listId) * Get an array of subscribers emails from a list * @param string $listId * @return array + * @throws MailchimpException */ - public function getSubscriberEmails($listId) + public function getSubscriberEmails(string $listId): array { $emails = []; $members = []; - $offset=0; + $offset = 0; $maxresult = 200; - $result = $this->mailchimp->get("lists/$listId/members", ['count'=> $maxresult]); + $result = $this->mailchimp->get('lists/' . $listId . '/members', ['count' => $maxresult]); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); @@ -345,20 +376,21 @@ public function getSubscriberEmails($listId) $members = array_merge($members, $result['members']); while ($offset < $totalItems) { - $offset+=$maxresult; - $result = $this->mailchimp->get("lists/$listId/members", [ - 'count' => $maxresult, - 'offset' => $offset - ]); + $offset += $maxresult; + $result = $this->mailchimp->get('lists/' . $listId . '/members', [ + 'count' => $maxresult, + 'offset' => $offset + ]); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); } + $members = array_merge($members, $result['members']); - }; + } foreach ($members as $key => $member) { - array_push($emails, $member['email_address']); + $emails[] = $member['email_address']; } return $emails; @@ -366,17 +398,18 @@ public function getSubscriberEmails($listId) /** * find all merge fields for a list - * http://developer.mailchimp.com/documentation/mailchimp/reference/lists/merge-fields/# + * https://mailchimp.com/developer/marketing/api/list-merges/ * @param string $listId * @return array + * @throws MailchimpException */ - public function getMergeFields($listId) + public function getMergeFields(string $listId): array { - $result = $this->mailchimp->get("lists/$listId/merge-fields"); + $result = $this->mailchimp->get('lists/' . $listId . '/merge-fields'); # Handle mailchimp default count limit if ($result['total_items'] > self::MAILCHIMP_DEFAULT_COUNT_LIMIT) { - $result = $this->mailchimp->get("lists/$listId/merge-fields", array("count" => $result['total_items'])); + $result = $this->mailchimp->get('lists/' . $listId . '/merge-fields', ['count' => $result['total_items']]); } if (!$this->mailchimp->success()) { @@ -388,14 +421,15 @@ public function getMergeFields($listId) /** * add merge field for a list - * http://developer.mailchimp.com/documentation/mailchimp/reference/lists/merge-fields/# + * https://mailchimp.com/developer/marketing/api/list-merges/ * @param string $listId * @param array $mergeData ["name" => '', "type" => ''] * @return array + * @throws MailchimpException */ - public function addMergeField($listId, array $mergeData) + public function addMergeField(string $listId, array $mergeData): array { - $result = $this->mailchimp->post("lists/$listId/merge-fields", $mergeData); + $result = $this->mailchimp->post('lists/' . $listId . '/merge-fields', $mergeData); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); @@ -406,15 +440,16 @@ public function addMergeField($listId, array $mergeData) /** * add merge field for a list - * http://developer.mailchimp.com/documentation/mailchimp/reference/lists/merge-fields/#edit-patch_lists_list_id_merge_fields_merge_id + * https://mailchimp.com/developer/marketing/api/list-merges/ * @param string $listId * @param string $mergeId * @param array $mergeData ["name" => '', "type" => '', ...] * @return array + * @throws MailchimpException */ - public function updateMergeField($listId, $mergeId, $mergeData) + public function updateMergeField(string $listId, string $mergeId, array $mergeData): array { - $result = $this->mailchimp->patch("lists/$listId/merge-fields/$mergeId", $mergeData); + $result = $this->mailchimp->patch('lists/' . $listId . '/merge-fields/' . $mergeId, $mergeData); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); @@ -424,15 +459,16 @@ public function updateMergeField($listId, $mergeId, $mergeData) } /** - * delete merge field for a list - * http://developer.mailchimp.com/documentation/mailchimp/reference/lists/merge-fields/# - * @param string $listId - * @param string $mergeId - * @return array - */ - public function deleteMergeField($listId, $mergeId) + * delete merge field for a list + * https://mailchimp.com/developer/marketing/api/list-merges/ + * @param string $listId + * @param string $mergeId + * @return array + * @throws MailchimpException + */ + public function deleteMergeField(string $listId, string $mergeId): array { - $result = $this->mailchimp->delete("lists/$listId/merge-fields/$mergeId"); + $result = $this->mailchimp->delete('lists/' . $listId . '/merge-fields/' . $mergeId); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); @@ -442,12 +478,13 @@ public function deleteMergeField($listId, $mergeId) } /** - * Automatically configure Webhook for a list - * @param string $listId - * @param string $webhookurl - * @return array - */ - public function registerMainWebhook($listId, $webhookurl) + * Automatically configure Webhook for a list + * @param string $listId + * @param string $webhookurl + * @return array + * @throws MailchimpException + */ + public function registerMainWebhook(string $listId, string $webhookurl): array { // Configure webhook $subscribeWebhook = [ @@ -463,7 +500,7 @@ public function registerMainWebhook($listId, $webhookurl) 'sources' => [ 'user' => true, 'admin' => true, - 'api' => false // to avoid double (infinite loop) update (update an subscriber with the API and the webhook reupdate the user, ...) + 'api' => false // to avoid double (infinite loop) update (update a subscriber with the API and the webhook reupdate the user, ...) ] ]; @@ -471,15 +508,16 @@ public function registerMainWebhook($listId, $webhookurl) } /** - * Add a new webhook to a list - * http://developer.mailchimp.com/documentation/mailchimp/reference/lists/webhooks/# - * @param string $listId - * @param array $webhookData - * @return array - */ - public function addWebhook($listId, array $webhookData) + * Add a new webhook to a list + * https://mailchimp.com/developer/marketing/api/list-webhooks/ + * @param string $listId + * @param array $webhookData + * @return array + * @throws MailchimpException + */ + public function addWebhook(string $listId, array $webhookData): array { - $result = $this->mailchimp->post("lists/$listId/webhooks", $webhookData); + $result = $this->mailchimp->post('lists/' . $listId . '/webhooks', $webhookData); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); @@ -489,14 +527,15 @@ public function addWebhook($listId, array $webhookData) } /** - * Get webhooks of a list - * http://developer.mailchimp.com/documentation/mailchimp/reference/lists/webhooks/# - * @param string $listId - * @return array - */ - public function getWebhooks($listId) + * Get webhooks of a list + * https://mailchimp.com/developer/marketing/api/list-webhooks/ + * @param string $listId + * @return array + * @throws MailchimpException + */ + public function getWebhooks(string $listId): array { - $result = $this->mailchimp->get("lists/$listId/webhooks"); + $result = $this->mailchimp->get('lists/' . $listId . '/webhooks'); if (!$this->mailchimp->success()) { $this->throwMailchimpError($this->mailchimp->getLastResponse()); @@ -507,31 +546,41 @@ public function getWebhooks($listId) /** * [throwMailchimpError description] - * @param array $errorResponse [description] + * @param array $errorResponse [description] * @return void * @throws MailchimpException [description] */ - private function throwMailchimpError(array $errorResponse) + private function throwMailchimpError(array $errorResponse): void { - $errorArray = json_decode($errorResponse['body'], true); - if (is_array($errorArray) && array_key_exists('errors', $errorArray)) { + try { + $errorArray = json_decode($errorResponse['body'], true, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { throw new MailchimpException( - $errorArray['status'], - $errorArray['detail'], - $errorArray['type'], - $errorArray['title'], - $errorArray['errors'], - $errorArray['instance'] + 400, + $e->getMessage(), + 'JSON', + 'Invalid JSON response.', ); - } else { + } + + if (is_array($errorArray) && array_key_exists('errors', $errorArray)) { throw new MailchimpException( $errorArray['status'], $errorArray['detail'], $errorArray['type'], $errorArray['title'], - null, + $errorArray['errors'], $errorArray['instance'] ); } + + throw new MailchimpException( + $errorArray['status'], + $errorArray['detail'], + $errorArray['type'], + $errorArray['title'], + null, + $errorArray['instance'] + ); } } diff --git a/src/Subscriber/ListSynchronizer.php b/src/Subscriber/ListSynchronizer.php index 55c6a23..e21cf65 100644 --- a/src/Subscriber/ListSynchronizer.php +++ b/src/Subscriber/ListSynchronizer.php @@ -2,6 +2,8 @@ namespace Welp\MailchimpBundle\Subscriber; +use Welp\MailchimpBundle\Exception\MailchimpException; + /** * Handle Synchronization between SubscriberList and specific MailChimp List */ @@ -10,7 +12,7 @@ class ListSynchronizer /** * @var ListRepository */ - protected $listRepository; + protected ListRepository $listRepository; /** * @param ListRepository $listRepository @@ -23,9 +25,10 @@ public function __construct(ListRepository $listRepository) /** * Synchronise user from provider with MailChimp List * @param SubscriberListInterface $list - * @return void + * @return array + * @throws MailchimpException */ - public function synchronize(SubscriberListInterface $list) + public function synchronize(SubscriberListInterface $list): array { $listData = $this->listRepository->findById($list->getListId()); @@ -34,6 +37,7 @@ public function synchronize(SubscriberListInterface $list) // unsubscribe difference $this->unsubscribeDifference($list->getListId(), $subscribers); + // subscribe the rest return $this->batchSubscribe($list->getListId(), $subscribers); } @@ -42,29 +46,31 @@ public function synchronize(SubscriberListInterface $list) * Subscribe a batch of user * @param string $listId * @param array $subscribers - * @return void + * @return array */ - protected function batchSubscribe($listId, array $subscribers = []) + protected function batchSubscribe(string $listId, array $subscribers = []): array { return $this->listRepository->batchSubscribe($listId, $subscribers); } /** - * Unsubscribe the difference between the array subscriber an user + * Unsubscribe the difference between the array subscriber a user * @param string $listId * @param array $subscribers * @return void + * @throws MailchimpException */ - protected function unsubscribeDifference($listId, array $subscribers) + protected function unsubscribeDifference(string $listId, array $subscribers): void { $mailchimpEmails = $this->listRepository->getSubscriberEmails($listId); - $internalEmails = array_map(function (Subscriber $subscriber) { + $internalEmails = array_map(static function (Subscriber $subscriber) { return $subscriber->getEmail(); }, $subscribers); // emails that are present in mailchimp but not internally should be unsubscribed $diffenceEmails = array_diff($mailchimpEmails, $internalEmails); - if (sizeof($diffenceEmails) == 0) { + + if (count($diffenceEmails) === 0) { return; } @@ -76,8 +82,9 @@ protected function unsubscribeDifference($listId, array $subscribers) * @param string $listId * @param array $mergeFields * @return void + * @throws MailchimpException */ - public function synchronizeMergeFields($listId, array $mergeFields = []) + public function synchronizeMergeFields(string $listId, array $mergeFields = []): void { $mailChimpMergeFields = $this->listRepository->getMergeFields($listId); @@ -99,21 +106,23 @@ public function synchronizeMergeFields($listId, array $mergeFields = []) } /** - * Test if the merge field Tag exists in an array - * @param string $tagName - * @param array $tags - * @return mixed (Boolean true|false) or $tag['merge_id'] - */ - protected function tagExists($tagName, array $tags) + * Test if the merge field Tag exists in an array + * @param string $tagName + * @param array $tags + * @return bool|string (Boolean true|false) or $tag['merge_id'] + */ + protected function tagExists(string $tagName, array $tags): bool|string { foreach ($tags as $tag) { - if ($tag['tag'] == $tagName) { + if ($tag['tag'] === $tagName) { if (array_key_exists('merge_id', $tag)) { return $tag['merge_id']; } + return true; } } + return false; } } diff --git a/src/Subscriber/Subscriber.php b/src/Subscriber/Subscriber.php index 93a96bc..a4d7fc2 100644 --- a/src/Subscriber/Subscriber.php +++ b/src/Subscriber/Subscriber.php @@ -4,7 +4,7 @@ /** * Class to represent a subscriber - * http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/ + * https://mailchimp.com/developer/marketing/api/list-members/ */ class Subscriber { @@ -12,19 +12,19 @@ class Subscriber * Subscriber's email * @var string */ - protected $email; + protected string $email; /** * Subscriber's merge fields * @var array */ - protected $mergeFields; + protected array $mergeFields; /** * Subscriber's options * @var array */ - protected $options; + protected array $options; /** * @@ -32,7 +32,7 @@ class Subscriber * @param array $mergeFields * @param array $options */ - public function __construct($email, array $mergeFields = [], array $options = []) + public function __construct(string $email, array $mergeFields = [], array $options = []) { $this->email = $email; $this->mergeFields = $mergeFields; @@ -40,10 +40,10 @@ public function __construct($email, array $mergeFields = [], array $options = [] } /** - * Formate Subscriber for MailChimp API request + * Format Subscriber for MailChimp API request * @return array */ - public function formatMailChimp() + public function formatMailChimp(): array { $options = $this->options; if (!empty($this->getMergeFields())) { @@ -61,7 +61,7 @@ public function formatMailChimp() * Correspond to email_address in MailChimp request * @return string */ - public function getEmail() + public function getEmail(): string { return $this->email; } @@ -71,9 +71,9 @@ public function getEmail() * @param array $mergeFields * @return array ['TAGKEY' => value, ...] */ - public function setMergeFields(array $mergeFields) + public function setMergeFields(array $mergeFields): array { - // since fev2017, MailChimp API doesn't handle null value en throw 400 + // since feb2017, MailChimp API doesn't handle null value en throw 400 // errors when you try to add subscriber with a mergefields value to null foreach ($mergeFields as $key => $value) { if ($value === null) { @@ -89,7 +89,7 @@ public function setMergeFields(array $mergeFields) * Correspond to merge_fields in MailChimp request * @return array ['TAGKEY' => value, ...] */ - public function getMergeFields() + public function getMergeFields(): array { // since fev2017, MailChimp API doesn't handle null value en throw 400 // errors when you try to add subscriber with a mergefields value to null @@ -98,6 +98,7 @@ public function getMergeFields() unset($this->mergeFields[$key]); } } + return $this->mergeFields; } @@ -106,7 +107,7 @@ public function getMergeFields() * email_type, interests, language, vip, location, ip_signup, timestamp_signup, ip_opt, timestamp_opt * @return array */ - public function getOptions() + public function getOptions(): array { return $this->options; } diff --git a/src/Subscriber/SubscriberList.php b/src/Subscriber/SubscriberList.php index 8aca8b5..9376478 100644 --- a/src/Subscriber/SubscriberList.php +++ b/src/Subscriber/SubscriberList.php @@ -14,39 +14,39 @@ class SubscriberList implements SubscriberListInterface * MailChimp ListId * @var string */ - protected $listId; + protected string $listId; /** * Subscriber provider * @var ProviderInterface */ - protected $provider; + protected ProviderInterface $provider; /** * Merge fields * @var array */ - protected $mergeFields; + protected array $mergeFields; /** * MailChimp webhook URL * @var string */ - protected $webhookUrl; - + protected string $webhookUrl = ''; + /** * MailChimp webhook secret * @var string */ - protected $webhookSecret; + protected string $webhookSecret = ''; /** * - * @param string $listId + * @param string $listId * @param ProviderInterface $provider * @param array $mergeFields */ - public function __construct($listId, ProviderInterface $provider, array $mergeFields = array()) + public function __construct(string $listId, ProviderInterface $provider, array $mergeFields = []) { $this->listId = $listId; $this->provider = $provider; @@ -55,14 +55,14 @@ public function __construct($listId, ProviderInterface $provider, array $mergeFi //If the provider implements DynamicProviderInterface, set the list id in the provider if ($this->provider instanceof DynamicProviderInterface) { $this->provider->setListId($this->listId); - } + } } /** * get the MailChimp ListId * @return string */ - public function getListId() + public function getListId(): string { return $this->listId; } @@ -71,7 +71,7 @@ public function getListId() * Get the subscriber provider * @return ProviderInterface */ - public function getProvider() + public function getProvider(): ProviderInterface { return $this->provider; } @@ -80,7 +80,7 @@ public function getProvider() * Get the list merge fields * @return array */ - public function getMergeFields() + public function getMergeFields(): array { return $this->mergeFields; } @@ -89,16 +89,16 @@ public function getMergeFields() * Get the list webhook URL * @return string */ - public function getWebhookUrl() + public function getWebhookUrl(): string { return $this->webhookUrl; } /** * Set the list webhook URL - * @param string + * @param string $webhookUrl */ - public function setWebhookUrl($webhookUrl) + public function setWebhookUrl(string $webhookUrl): void { $this->webhookUrl = $webhookUrl; } @@ -107,16 +107,16 @@ public function setWebhookUrl($webhookUrl) * Get the list webhook secret * @return string */ - public function getWebhookSecret() + public function getWebhookSecret(): string { return $this->webhookSecret; } /** * Set the list webhook secret - * @param string + * @param string $webhookSecret */ - public function setWebhookSecret($webhookSecret) + public function setWebhookSecret(string $webhookSecret): void { $this->webhookSecret = $webhookSecret; } diff --git a/src/Subscriber/SubscriberListInterface.php b/src/Subscriber/SubscriberListInterface.php index 001a60b..a6683f3 100644 --- a/src/Subscriber/SubscriberListInterface.php +++ b/src/Subscriber/SubscriberListInterface.php @@ -2,6 +2,8 @@ namespace Welp\MailchimpBundle\Subscriber; +use Welp\MailchimpBundle\Provider\ProviderInterface; + /** * SubscriberList interface linked a MailChimpList with a SubscriberProvider */ @@ -11,41 +13,41 @@ interface SubscriberListInterface * get the MailChimp ListId * @return string */ - public function getListId(); + public function getListId(): string; /** * Get the subscriber provider * @return ProviderInterface */ - public function getProvider(); + public function getProvider(): ProviderInterface; /** * Get the list merge fields * @return array */ - public function getMergeFields(); + public function getMergeFields(): array; /** * Get the list webhook URL * @return string */ - public function getWebhookUrl(); + public function getWebhookUrl(): string; /** * Set the list webhook URL - * @param string + * @param string $webhookUrl */ - public function setWebhookUrl($webhookUrl); + public function setWebhookUrl(string $webhookUrl); /** * Get the list webhook secret * @return string */ - public function getWebhookSecret(); + public function getWebhookSecret(): string; /** * Set the list webhook secret - * @param string + * @param string $webhookSecret */ - public function setWebhookSecret($webhookSecret); + public function setWebhookSecret(string $webhookSecret); } diff --git a/src/WelpMailchimpBundle.php b/src/WelpMailchimpBundle.php index a1136e1..bd0a1f3 100644 --- a/src/WelpMailchimpBundle.php +++ b/src/WelpMailchimpBundle.php @@ -1,13 +1,21 @@