Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

Commit

Permalink
Allow usage of Reader without GooglePlayPodcast extension
Browse files Browse the repository at this point in the history
Custom implementations of `ExtensionManagerInterface` may not have _new
core_ extensions present. As a result, when upgrading, an exception is
thrown due to inability to load the new extension.

This was discovered with the 2.10 release, when we added a new core
extension, GooglePlayPodcast; see #80 and https://www.drupal.org/project/drupal/issues/2976335
for more details.

As such, we now check for _new_ core extensions before registering them.
When we discover they are not in the extension manager, we emit an
`E_USER_NOTICE` indicating the user should update their
`ExtensionManagerInterface` implementation to add entries for the new
extension, but then continue on without registering the extension. This
ensures no exception is thrown.
  • Loading branch information
weierophinney committed Jun 18, 2018
1 parent 875414e commit dfbb10f
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 5 deletions.
50 changes: 45 additions & 5 deletions src/Reader/Reader.php
Expand Up @@ -577,19 +577,24 @@ public static function getExtensionManager()
*/
public static function registerExtension($name)
{
$manager = static::getExtensionManager();
$feedName = $name . '\Feed';
$entryName = $name . '\Entry';
$manager = static::getExtensionManager();

if (static::isRegistered($name)) {
if ($manager->has($feedName) || $manager->has($entryName)) {
return;
}
}

if (! $manager->has($feedName) && ! $manager->has($entryName)) {
throw new Exception\RuntimeException('Could not load extension: ' . $name
. ' using Plugin Loader. Check prefix paths are configured and extension exists.');
if (! static::hasExtension($name)) {
throw new Exception\RuntimeException(sprintf(
'Could not load extension "%s" using Plugin Loader.'
. ' Check prefix paths are configured and extension exists.',
$name
));
}

if ($manager->has($feedName)) {
static::$extensions['feed'][] = $feedName;
}
Expand Down Expand Up @@ -672,7 +677,18 @@ protected static function registerCoreExtensions()
static::registerExtension('WellFormedWeb');
static::registerExtension('Thread');
static::registerExtension('Podcast');
static::registerExtension('GooglePlayPodcast');

// Added in 2.10.0; check for it conditionally
static::hasExtension('GooglePlayPodcast')
? static::registerExtension('GooglePlayPodcast')
: trigger_error(
sprintf(
'Please update your %1$s\ExtensionManagerInterface implementation to add entries for'
. ' %1$s\Extension\GooglePlayPodcast\Entry and %1$s\Extension\GooglePlayPodcast\Feed.',
__NAMESPACE__
),
\E_USER_NOTICE
);
}

/**
Expand All @@ -693,4 +709,28 @@ public static function arrayUnique(array $array)
}
return $array;
}

/**
* Does the extension manager have the named extension?
*
* This method exists to allow us to test if an extension is present in the
* extension manager. It may be used by registerExtension() to determine if
* the extension has items present in the manager, or by
* registerCoreExtension() to determine if the core extension has entries
* in the extension manager. In the latter case, this can be useful when
* adding new extensions in a minor release, as custom extension manager
* implementations may not yet have an entry for the extension, which would
* then otherwise cause registerExtension() to fail.
*
* @param string $name
* @return bool
*/
protected static function hasExtension($name)
{
$feedName = $name . '\Feed';
$entryName = $name . '\Entry';
$manager = static::getExtensionManager();

return $manager->has($feedName) || $manager->has($entryName);
}
}
29 changes: 29 additions & 0 deletions test/Reader/ReaderTest.php
Expand Up @@ -353,6 +353,35 @@ public function testSetHttpClientThrowsException()
Reader\Reader::setHttpClient(new stdClass);
}

public function testReaderEmitsNoticeDuringFeedImportWhenGooglePlayPodcastExtensionUnavailable()
{
Reader\Reader::setExtensionManager(new TestAsset\CustomExtensionManager());

$notices = (object) [
'messages' => [],
];

set_error_handler(function ($errno, $errstr) use ($notices) {
$notices->messages[] = $errstr;
}, \E_USER_NOTICE);
$feed = Reader\Reader::importFile(
dirname(__FILE__) . '/Entry/_files/Atom/title/plain/atom10.xml'
);
restore_error_handler();

$message = array_reduce($notices->messages, function ($toReturn, $message) {
if ('' !== $toReturn) {
return $toReturn;
}
return false === strstr($message, 'GooglePlayPodcast') ? '' : $message;
}, '');

$this->assertNotEmpty(
$message,
'GooglePlayPodcast extension was present in extension manager, but was not expected to be'
);
}

// @codingStandardsIgnoreStart
protected function _getTempDirectory()
{
Expand Down
57 changes: 57 additions & 0 deletions test/Reader/TestAsset/CustomExtensionManager.php
@@ -0,0 +1,57 @@
<?php
/**
* @see https://github.com/zendframework/zend-feed for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-feed/blob/master/LICENSE.md New BSD License
*/

namespace ZendTest\Feed\Reader\TestAsset;

use Zend\Feed\Reader\Exception\InvalidArgumentException;
use Zend\Feed\Reader\Extension;
use Zend\Feed\Reader\ExtensionManagerInterface;

/**
* Standalone extension manager that omits any extensions added after the 2.9 series.
*/
class CustomExtensionManager implements ExtensionManagerInterface
{
private $extensions = [
'Atom\Entry' => Extension\Atom\Entry::class,
'Atom\Feed' => Extension\Atom\Feed::class,
'Content\Entry' => Extension\Content\Entry::class,
'CreativeCommons\Entry' => Extension\CreativeCommons\Entry::class,
'CreativeCommons\Feed' => Extension\CreativeCommons\Feed::class,
'DublinCore\Entry' => Extension\DublinCore\Entry::class,
'DublinCore\Feed' => Extension\DublinCore\Feed::class,
'Podcast\Entry' => Extension\Podcast\Entry::class,
'Podcast\Feed' => Extension\Podcast\Feed::class,
'Slash\Entry' => Extension\Slash\Entry::class,
'Syndication\Feed' => Extension\Syndication\Feed::class,
'Thread\Entry' => Extension\Thread\Entry::class,
'WellFormedWeb\Entry' => Extension\WellFormedWeb\Entry::class,
];

/**
* Do we have the extension?
*
* @param string $extension
* @return bool
*/
public function has($extension)
{
return array_key_exists($extension, $this->extensions);
}

/**
* Retrieve the extension
*
* @param string $extension
* @return Extension\AbstractEntry|Extension\AbstractFeed
*/
public function get($extension)
{
$class = $this->extensions[$extension];
return new $class();
}
}

0 comments on commit dfbb10f

Please sign in to comment.