Version Description
2013/12/10 =
FEATURE: New "Reporting" add-on - more sophisticated/flexible backup reports (http://updraftplus.com/shop/reporting/)
FEATURE: New enhanced add-on for Rackspace Cloud Files users, allowing them to create a new sub-user with exclusive access to the backup container (http://updraftplus.com/shop/cloudfiles-enhanced/) (PHP 5.3.3+ required for this feature)
FEATURE: Add region-selection (Dallas/Chicago/Northern Virginia/Sydney/Hong Kong) to Rackspace Cloud Files (PHP 5.3.3+ required for this feature)
FEATURE: Add option to 'Backup Now' dialog to not despatch this backup to the cloud
FIX: Fix bug in restore of wpcore (Premium) with certain options when backup set was from a previously restored backup with the same certain options
FIX: After restoring a site, only delete the backup set from local storage if it was also stored in the cloud (prevents the user having to upload the backup set twice if they want to re-run the restore)
FIX: Improve detection of extremely long-running/slow jobs
FIX: Fix issue with Rackspace Cloudfiles on WPMU installs
TWEAK: Mark as tested up to WordPress 3.8
TWEAK: Restore operations are now logged
TWEAK: Detect the database connection dropping and recover (seen on a very slow site where PHP ran continuously for 30 mins)
TWEAK: Change how permalinks are flushed post-restore. This spares the user from having to manually visit the permalinks page if they had plugins that altered their permalink structure (e.g. WooCommerce).
TWEAK: Require fewer file permissions when restoring/migrating
TWEAK: Remove various spurious PHP notices caught by the post-1.7.41 extra logging
TWEAK: Compress the log file before emailing it, if it is over 6Mb
TWEAK: Make sure some potential error messages from Dropbox are displayed properly
TWEAK: Work around sites with site/home URL settings in the WP DB that erroneously have a trailing slash
TWEAK: Log PHP notices for all job types
Release Info
Developer | DavidAnderson |
Plugin | ![]() |
Version | 1.8.1 |
Comparing to | |
See all releases |
Code changes from version 1.7.41 to 1.8.1
- admin.php +171 -85
- backup.php +109 -39
- images/rackspacecloud-logo.png +0 -0
- includes/Dropbox/OAuth/Consumer/ConsumerAbstract.php +11 -9
- includes/Dropbox/OAuth/Consumer/Curl.php +10 -5
- includes/php-opencloud/autoload.php +7 -0
- includes/php-opencloud/composer/ClassLoader.php +246 -0
- includes/php-opencloud/composer/autoload_classmap.php +9 -0
- includes/php-opencloud/composer/autoload_namespaces.php +15 -0
- includes/php-opencloud/composer/autoload_real.php +43 -0
- includes/php-opencloud/composer/installed.json +321 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/AbstractHasDispatcher.php +49 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/Collection.php +403 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/Event.php +52 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/Exception/BadMethodCallException.php +5 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/Exception/ExceptionCollection.php +85 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/Exception/GuzzleException.php +8 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/Exception/InvalidArgumentException.php +5 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/Exception/RuntimeException.php +5 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/Exception/UnexpectedValueException.php +5 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/FromConfigInterface.php +18 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/HasDispatcherInterface.php +54 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/ToArrayInterface.php +16 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/Version.php +29 -0
- includes/php-opencloud/guzzle/common/Guzzle/Common/composer.json +20 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/AbstractEntityBodyDecorator.php +221 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/CachingEntityBody.php +229 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Client.php +506 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/ClientInterface.php +223 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Curl/CurlHandle.php +451 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Curl/CurlMulti.php +363 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Curl/CurlMultiInterface.php +58 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Curl/CurlMultiProxy.php +147 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Curl/CurlVersion.php +66 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Curl/RequestMediator.php +147 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/EntityBody.php +201 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/EntityBodyInterface.php +73 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Exception/BadResponseException.php +70 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Exception/ClientErrorResponseException.php +8 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Exception/CouldNotRewindStreamException.php +7 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Exception/CurlException.php +101 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Exception/HttpException.php +10 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Exception/MultiTransferException.php +145 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Exception/RequestException.php +39 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Exception/ServerErrorResponseException.php +8 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Exception/TooManyRedirectsException.php +5 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/IoEmittingEntityBody.php +83 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/AbstractMessage.php +220 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/EntityEnclosingRequest.php +248 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/EntityEnclosingRequestInterface.php +136 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/Header.php +178 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/Header/CacheControl.php +121 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/Header/HeaderCollection.php +109 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/Header/HeaderFactory.php +26 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/Header/HeaderFactoryInterface.php +19 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/Header/HeaderInterface.php +83 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/Header/Link.php +93 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/MessageInterface.php +102 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/PostFile.php +109 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/PostFileInterface.php +67 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/Request.php +638 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/RequestFactory.php +356 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/RequestFactoryInterface.php +105 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/RequestInterface.php +318 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Message/Response.php +957 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Mimetypes.php +962 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/QueryAggregator/CommaAggregator.php +20 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/QueryAggregator/DuplicateAggregator.php +22 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/QueryAggregator/PhpAggregator.php +27 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php +22 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/QueryString.php +276 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/ReadLimitEntityBody.php +105 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/RedirectPlugin.php +250 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Resources/cacert.pem +3554 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Resources/cacert.pem.md5 +1 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/StaticClient.php +157 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/Url.php +531 -0
- includes/php-opencloud/guzzle/http/Guzzle/Http/composer.json +32 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/Cookie/CookieParser.php +86 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/Cookie/CookieParserInterface.php +33 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/Message/AbstractMessageParser.php +58 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/Message/MessageParser.php +110 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/Message/MessageParserInterface.php +27 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/Message/PeclHttpMessageParser.php +48 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/ParserRegistry.php +75 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/UriTemplate/PeclUriTemplate.php +26 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/UriTemplate/UriTemplate.php +243 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/UriTemplate/UriTemplateInterface.php +21 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/Url/UrlParser.php +48 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/Url/UrlParserInterface.php +19 -0
- includes/php-opencloud/guzzle/parser/Guzzle/Parser/composer.json +19 -0
- includes/php-opencloud/guzzle/stream/Guzzle/Stream/PhpStreamRequestFactory.php +270 -0
- includes/php-opencloud/guzzle/stream/Guzzle/Stream/Stream.php +289 -0
- includes/php-opencloud/guzzle/stream/Guzzle/Stream/StreamInterface.php +218 -0
- includes/php-opencloud/guzzle/stream/Guzzle/Stream/StreamRequestFactoryInterface.php +24 -0
- includes/php-opencloud/guzzle/stream/Guzzle/Stream/composer.json +30 -0
- includes/php-opencloud/rackspace/php-opencloud/.gitignore +13 -0
- includes/php-opencloud/rackspace/php-opencloud/.travis.yml +18 -0
- includes/php-opencloud/rackspace/php-opencloud/CONTRIBUTING.md +36 -0
- includes/php-opencloud/rackspace/php-opencloud/LICENSE +16 -0
- includes/php-opencloud/rackspace/php-opencloud/README.md +70 -0
- includes/php-opencloud/rackspace/php-opencloud/composer.json +30 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Autoscale/Resource/AbstractResource.php +89 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Autoscale/Resource/Group.php +194 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Autoscale/Resource/GroupConfiguration.php +52 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Autoscale/Resource/LaunchConfiguration.php +62 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Autoscale/Resource/ScalingPolicy.php +61 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Autoscale/Resource/Webhook.php +43 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Autoscale/Service.php +58 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/AgentException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/AlarmException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/CheckException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/CloudMonitoringException.php +17 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/EntityException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/MetricException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/NotificationHistoryException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/NotificationPlanException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/ServiceException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/TestException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Exception/ZoneException.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/AbstractResource.php +106 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/Agent.php +75 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/AgentConnection.php +30 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/AgentHost.php +51 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/AgentHostInfo.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/AgentTarget.php +61 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/AgentToken.php +32 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/Alarm.php +138 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/Changelog.php +30 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/Check.php +208 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/CheckType.php +45 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/Entity.php +93 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/Metric.php +21 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/Notification.php +74 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/NotificationHistory.php +56 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/NotificationPlan.php +55 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/NotificationType.php +26 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/ReadOnlyResource.php +36 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/View.php +47 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Resource/Zone.php +70 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/CloudMonitoring/Service.php +279 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Base.php +426 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Collection.php +408 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Collection/ArrayCollection.php +118 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Collection/PaginatedIterator.php +293 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Collection/ResourceIterator.php +217 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Constants/Datetime.php +35 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Constants/Header.php +65 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Constants/Mime.php +17 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Constants/Service.php +19 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Constants/Size.php +20 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Constants/State.php +21 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/AsyncError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/AsyncHttpError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/AsyncTimeoutError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/AttributeError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/AuthenticationError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/BaseException.php +7 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/CdnError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/CdnHttpError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/CdnNotAvailableError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/CdnTtlError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/CollectionException.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ContainerCreateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ContainerDeleteError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ContainerError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ContainerNameError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ContainerNotEmptyError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ContainerNotFoundError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/CreateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/CreateUpdateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/CredentialError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/DatabaseCreateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/DatabaseDeleteError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/DatabaseListError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/DatabaseNameError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/DatabaseUpdateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/DeleteError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/DocumentError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/DomainError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/EmptyResponseError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/EndpointError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/FlavorError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/HttpError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/HttpForbiddenError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/HttpOverLimitError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/HttpRetryError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/HttpTimeoutError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/HttpUnauthorizedError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/HttpUrlError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/IOError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/IdRequiredError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ImageError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InstanceCreateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InstanceDeleteError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InstanceError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InstanceFlavorError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InstanceNotFound.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InstanceUpdateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InvalidArgumentError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InvalidIdTypeError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InvalidIpTypeError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InvalidParameterError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/InvalidRequestError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/JsonError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/LoggingException.php +16 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/MetadataCreateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/MetadataDeleteError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/MetadataError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/MetadataJsonError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/MetadataKeyError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/MetadataPrefixError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/MetadataUpdateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/MisMatchedChecksumError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/MissingValueError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/NameError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/NetworkCreateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/NetworkDeleteError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/NetworkError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/NetworkUpdateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/NetworkUrlError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/NoContentTypeError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/NoNameError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ObjFetchError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ObjectCopyError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ObjectError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/RebuildError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/RecordTypeError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ResourceBucketException.php +16 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/RuntimeException.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ServerActionError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ServerCreateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ServerDeleteError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ServerImageScheduleError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ServerIpsError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ServerJsonError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ServerUpdateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ServerUrlError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/ServiceException.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/SnapshotError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/TempUrlMethodError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UnknownError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UnknownParameterError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UnrecognizedServiceError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UnsupportedExtensionError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UnsupportedFeatureExtension.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UnsupportedVersionError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UpdateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UrlError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UserCreateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UserDeleteError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UserListError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UserNameError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/UserUpdateError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/VolumeError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Exceptions/VolumeTypeError.php +5 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Http/Client.php +52 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Http/Message/Formatter.php +44 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Http/Message/RequestSubscriber.php +69 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Identity/Role.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Identity/Tenant.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Identity/User.php +15 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Lang.php +29 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Log/AbstractLogger.php +140 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Log/LogLevel.php +38 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Log/Logger.php +235 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Log/LoggerInterface.php +134 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Metadata.php +110 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/PersistentObject.php +726 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Service/AbstractService.php +417 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Service/Catalog.php +60 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Service/CatalogItem.php +146 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Service/Endpoint.php +112 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Service/NovaService.php +66 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Common/Service/ServiceBuilder.php +39 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Constants/Network.php +26 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Constants/ServerState.php +98 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Exception/KeyPairException.php +13 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Resource/Flavor.php +51 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Resource/Image.php +59 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Resource/KeyPair.php +124 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Resource/Network.php +116 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Resource/Server.php +661 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Resource/ServerMetadata.php +187 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Resource/VolumeAttachment.php +76 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Compute/Service.php +179 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/DNS/Resource/AsyncResponse.php +98 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/DNS/Resource/Domain.php +241 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/DNS/Resource/Object.php +134 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/DNS/Resource/PtrRecord.php +125 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/DNS/Resource/Record.php +56 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/DNS/Resource/Subdomain.php +26 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/DNS/Service.php +159 -0
- includes/php-opencloud/rackspace/php-opencloud/lib/OpenCloud/Database/Resource/Database.php +6 -0
@@ -74,16 +74,20 @@ class UpdraftPlus_Admin {
|
|
74 |
|
75 |
if (version_compare($wp_version, '3.2', '<')) add_action('all_admin_notices', array($this, 'show_admin_warning_wordpressversion'));
|
76 |
|
77 |
-
wp_enqueue_script('updraftplus-admin-ui', UPDRAFTPLUS_URL.'/includes/updraft-admin-ui.js', array('jquery', 'jquery-ui-dialog', 'plupload-all'), '
|
78 |
|
79 |
wp_localize_script( 'updraftplus-admin-ui', 'updraftlion', array(
|
80 |
'sendonlyonwarnings' => __('Send a report only when there are warnings/errors', 'updraftplus'),
|
81 |
-
'wholebackup' => __('
|
|
|
82 |
'rescanning' => __('Rescanning (looking for backups that you have uploaded manually into the internal backup store)...','updraftplus'),
|
|
|
83 |
'excludedeverything' => __('If you exclude both the database and the files, then you have excluded everything!', 'updraftplus'),
|
84 |
'restoreproceeding' => __('The restore operation has begun. Do not press stop or close your browser until it reports itself as having finished.', 'updraftplus'),
|
85 |
'unexpectedresponse' => __('Unexpected response:','updraftplus'),
|
86 |
'servererrorcode' => __('The web server returned an error code (try again, or check your web server logs)', 'updraftplus'),
|
|
|
|
|
87 |
'calculating' => __('calculating...','updraftplus'),
|
88 |
'begunlooking' => __('Begun looking for this entity','updraftplus'),
|
89 |
'stilldownloading' => __('Some files are still downloading or being processed - please wait.', 'updraftplus'),
|
@@ -101,6 +105,7 @@ class UpdraftPlus_Admin {
|
|
101 |
'notunderstood' => __('Download error: the server sent us a response which we did not understand.', 'updraftplus'),
|
102 |
'requeststart' => __('Requesting start of backup...', 'updraftplus'),
|
103 |
'phpinfo' => __('PHP information', 'updraftplus'),
|
|
|
104 |
'raw' => __('Raw backup history', 'updraftplus'),
|
105 |
'notarchive' => __('This file does not appear to be an UpdraftPlus backup archive (such files are .zip or .gz files which have a name like: backup_(time)_(site name)_(code)_(type).(zip|gz)). However, UpdraftPlus archives are standard zip/SQL files - so if you are sure that your file has the right format, then you can rename it to match that pattern.','updraftplus'),
|
106 |
'makesure' => __('(make sure that you were trying to upload a zip file previously created by UpdraftPlus)','updraftplus'),
|
@@ -115,6 +120,7 @@ class UpdraftPlus_Admin {
|
|
115 |
'backupnow' => __('Backup Now', 'updraftplus'),
|
116 |
'cancel' => __('Cancel', 'updraftplus'),
|
117 |
'deletebutton' => __('Delete', 'updraftplus'),
|
|
|
118 |
'close' => __('Close', 'updraftplus'),
|
119 |
'restore' => __('Restore', 'updraftplus'),
|
120 |
) );
|
@@ -123,7 +129,7 @@ class UpdraftPlus_Admin {
|
|
123 |
|
124 |
function core_upgrade_preamble() {
|
125 |
if (!class_exists('UpdraftPlus_Addon_Autobackup')) {
|
126 |
-
if (defined('
|
127 |
# TODO: Remove legacy/wrong use of transient any time from 1 Jun 2014
|
128 |
if (true == get_transient('updraftplus_dismissedautobackup')) return;
|
129 |
$dismissed_until = UpdraftPlus_Options::get_updraft_option('updraftplus_dismissedautobackup', 0);
|
@@ -190,6 +196,9 @@ class UpdraftPlus_Admin {
|
|
190 |
var updraft_downloader_nonce = '<?php wp_create_nonce("updraftplus_download"); ?>'
|
191 |
</script>
|
192 |
<style type="text/css">
|
|
|
|
|
|
|
193 |
.updraftplus-remove a {
|
194 |
color: red;
|
195 |
}
|
@@ -260,7 +269,7 @@ class UpdraftPlus_Admin {
|
|
260 |
|
261 |
function admin_action_upgrade_pluginortheme() {
|
262 |
|
263 |
-
if (isset($_GET['action']) && ($_GET['action'] == 'upgrade-plugin' || $_GET['action'] == 'upgrade-theme') && !class_exists('UpdraftPlus_Addon_Autobackup') && !defined('
|
264 |
|
265 |
# TODO: Remove legacy/erroneous use of transient any time after 1 Jun 2014
|
266 |
$dismissed = get_transient('updraftplus_dismissedautobackup');
|
@@ -364,7 +373,7 @@ class UpdraftPlus_Admin {
|
|
364 |
if ($_REQUEST['type'] == $type) $type_match = true;
|
365 |
}
|
366 |
|
367 |
-
if (!$type_match && $_REQUEST['type']
|
368 |
|
369 |
// Get the information on what is wanted
|
370 |
$type = $_REQUEST['type'];
|
@@ -406,6 +415,8 @@ class UpdraftPlus_Admin {
|
|
406 |
// Note that log() assumes that the data is in _POST, not _GET
|
407 |
if ($debug_mode) $updraftplus->logfile_open($updraftplus->nonce);
|
408 |
|
|
|
|
|
409 |
$updraftplus->log("Requested to obtain file: timestamp=$timestamp, type=$type, index=$findex");
|
410 |
|
411 |
$itext = (empty($findex)) ? '' : $findex;
|
@@ -414,7 +425,7 @@ class UpdraftPlus_Admin {
|
|
414 |
$services = (isset($backup_history[$timestamp]['service'])) ? $backup_history[$timestamp]['service'] : false;
|
415 |
if (is_string($services)) $services = array($services);
|
416 |
|
417 |
-
$updraftplus->jobdata_set('service', $
|
418 |
|
419 |
// Fetch it from the cloud, if we have not already got it
|
420 |
|
@@ -447,12 +458,14 @@ class UpdraftPlus_Admin {
|
|
447 |
$is_downloaded = false;
|
448 |
foreach ($services as $service) {
|
449 |
if ($is_downloaded) continue;
|
450 |
-
$this->download_file($file, $service);
|
451 |
-
if (is_readable($fullpath)) {
|
452 |
clearstatcache();
|
453 |
$updraftplus->log('Remote fetch was successful (file size: '.round(filesize($fullpath)/1024,1).' Kb)');
|
454 |
$is_downloaded = true;
|
455 |
} else {
|
|
|
|
|
456 |
$updraftplus->log('Remote fetch failed');
|
457 |
}
|
458 |
}
|
@@ -470,8 +483,10 @@ class UpdraftPlus_Admin {
|
|
470 |
$updraftplus->log('Remote fetch failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then remote retrieval may have failed.');
|
471 |
}
|
472 |
|
|
|
|
|
473 |
@fclose($updraftplus->logfile_handle);
|
474 |
-
|
475 |
|
476 |
exit;
|
477 |
|
@@ -492,10 +507,11 @@ class UpdraftPlus_Admin {
|
|
492 |
$objname = "UpdraftPlus_BackupModule_${service}";
|
493 |
if (method_exists($objname, "download")) {
|
494 |
$remote_obj = new $objname;
|
495 |
-
$remote_obj->download($file);
|
496 |
} else {
|
497 |
$updraftplus->log("Automatic backup restoration is not available with the method: $service.");
|
498 |
$updraftplus->log("$file: ".sprintf(__("The backup archive for this file could not be found. The remote storage method in use (%s) does not allow us to retrieve files. To perform any restoration using UpdraftPlus, you will need to obtain a copy of this file and place it inside UpdraftPlus's working folder", 'updraftplus'), $service)." (".$this->prune_updraft_dir_prefix($updraftplus->backups_dir_location()).")", 'error');
|
|
|
499 |
}
|
500 |
|
501 |
}
|
@@ -635,7 +651,10 @@ class UpdraftPlus_Admin {
|
|
635 |
$updraftplus->jobdata_set('job_type', 'delete');
|
636 |
$updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms);
|
637 |
|
638 |
-
if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode'))
|
|
|
|
|
|
|
639 |
|
640 |
$updraft_dir = $updraftplus->backups_dir_location();
|
641 |
$backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
|
@@ -715,6 +734,11 @@ class UpdraftPlus_Admin {
|
|
715 |
|
716 |
print json_encode(array('result' => 'success', 'message' => $message));
|
717 |
|
|
|
|
|
|
|
|
|
|
|
718 |
} elseif ('rawbackuphistory' == $_REQUEST['subaction']) {
|
719 |
echo '<h3>'.__('Known backups (raw)', 'updraftplus').'</h3><pre>';
|
720 |
var_dump($updraftplus->get_backup_history());
|
@@ -761,12 +785,17 @@ class UpdraftPlus_Admin {
|
|
761 |
} elseif ('ping' == $_REQUEST['subaction']) {
|
762 |
// The purpose of this is to detect brokenness caused by extra line feeds in plugins/themes - before it breaks other AJAX operations and leads to support requests
|
763 |
echo 'pong';
|
|
|
|
|
764 |
} elseif ('phpinfo' == $_REQUEST['subaction']) {
|
765 |
phpinfo(INFO_ALL ^ (INFO_CREDITS | INFO_LICENSE));
|
|
|
|
|
766 |
} elseif ('backupnow' == $_REQUEST['subaction']) {
|
767 |
echo '<strong>',__('Schedule backup','updraftplus').':</strong> ';
|
|
|
768 |
$event = (!empty($_REQUEST['backupnow_nofiles'])) ? 'updraft_backupnow_backup_database' : ((!empty($_REQUEST['backupnow_nodb'])) ? 'updraft_backupnow_backup' : 'updraft_backupnow_backup_all');
|
769 |
-
if (wp_schedule_single_event(time()+5, $event) === false) {
|
770 |
$updraftplus->log("A backup run failed to schedule");
|
771 |
echo __("Failed.",'updraftplus')."</div>";
|
772 |
} else {
|
@@ -895,7 +924,7 @@ class UpdraftPlus_Admin {
|
|
895 |
|
896 |
$db_file = (is_string($backup['db'])) ? $updraft_dir.'/'.$backup['db'] : $updraft_dir.'/'.$backup['db'][0];
|
897 |
|
898 |
-
if (!is_readable($db_file)) return;
|
899 |
|
900 |
// Encrypted - decrypt it
|
901 |
if ($updraftplus->is_db_encrypted($db_file)) {
|
@@ -952,24 +981,26 @@ class UpdraftPlus_Admin {
|
|
952 |
|
953 |
$migration_warning = false;
|
954 |
|
|
|
|
|
|
|
955 |
while (!gzeof($dbhandle) && ($line<100 || count($wanted_tables)>0)) {
|
956 |
$line++;
|
957 |
// Up to 1Mb
|
958 |
$buffer = rtrim(gzgets($dbhandle, 1048576));
|
959 |
// Comments are what we are interested in
|
960 |
if (substr($buffer, 0, 1) == '#') {
|
961 |
-
|
962 |
if ('' == $old_siteurl && preg_match('/^\# Backup of: (http(.*))$/', $buffer, $matches)) {
|
963 |
-
$old_siteurl = $matches[1];
|
964 |
$mess[] = __('Backup of:', 'updraftplus').' '.htmlspecialchars($old_siteurl).((!empty($old_wp_version)) ? ' '.sprintf(__('(version: %s)', 'updraftplus'), $old_wp_version) : '');
|
965 |
// Check for should-be migration
|
966 |
-
if (!$migration_warning && $old_siteurl != site_url()) {
|
967 |
$migration_warning = true;
|
968 |
$powarn = apply_filters('updraftplus_dbscan_urlchange', sprintf(__('Warning: %s', 'updraftplus'), '<a href="http://updraftplus.com/shop/migrator/">'.__('This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus').'</a>'), $old_siteurl, $res);
|
969 |
if (!empty($powarn)) $warn[] = $powarn;
|
970 |
}
|
971 |
} elseif ('' == $old_home && preg_match('/^\# Home URL: (http(.*))$/', $buffer, $matches)) {
|
972 |
-
$old_home = $matches[1];
|
973 |
// Check for should-be migration
|
974 |
if (!$migration_warning && $old_home != home_url()) {
|
975 |
$migration_warning = true;
|
@@ -1265,10 +1296,13 @@ CREATE TABLE $wpdb->signups (
|
|
1265 |
// If we restored the database, then that will have out-of-date information which may confuse the user - so automatically re-scan for them.
|
1266 |
$this->rebuild_backup_history();
|
1267 |
echo '<p><strong>'.__('Restore successful!','updraftplus').'</strong></p>';
|
|
|
1268 |
echo '<b>'.__('Actions','updraftplus').':</b> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&updraft_restore_success=true">'.__('Return to UpdraftPlus Configuration','updraftplus').'</a>';
|
1269 |
return;
|
1270 |
} elseif (is_wp_error($backup_success)) {
|
1271 |
echo '<p>Restore failed...</p>';
|
|
|
|
|
1272 |
$updraftplus->list_errors();
|
1273 |
echo '<b>Actions:</b> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus">'.__('Return to UpdraftPlus Configuration','updraftplus').'</a>';
|
1274 |
return;
|
@@ -1277,21 +1311,11 @@ CREATE TABLE $wpdb->signups (
|
|
1277 |
return;
|
1278 |
}
|
1279 |
}
|
1280 |
-
$deleted_old_dirs = false;
|
1281 |
-
if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_delete_old_dirs') {
|
1282 |
-
|
1283 |
-
echo '<h1>UpdraftPlus - '.__('Remove old directories','updraftplus').'</h1>';
|
1284 |
|
|
|
1285 |
$nonce = (empty($_REQUEST['_wpnonce'])) ? "" : $_REQUEST['_wpnonce'];
|
1286 |
-
if (!wp_verify_nonce($nonce, '
|
1287 |
-
|
1288 |
-
if($this->delete_old_dirs()) {
|
1289 |
-
echo '<p>'.__('Old directories successfully removed.','updraftplus').'</p><br/>';
|
1290 |
-
$deleted_old_dirs = true;
|
1291 |
-
} else {
|
1292 |
-
echo '<p>',__('Old directory removal failed for some reason. You may want to do this manually.','updraftplus').'</p><br/>';
|
1293 |
-
}
|
1294 |
-
echo '<b>'.__('Actions','updraftplus').':</b> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus">'.__('Return to UpdraftPlus Configuration','updraftplus').'</a>';
|
1295 |
return;
|
1296 |
}
|
1297 |
|
@@ -1314,6 +1338,8 @@ CREATE TABLE $wpdb->signups (
|
|
1314 |
return;
|
1315 |
}
|
1316 |
|
|
|
|
|
1317 |
echo '<div id="updraft_backup_started" class="updated fade" style="display:none; max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;"></div>';
|
1318 |
|
1319 |
// updraft_file_ids is not deleted
|
@@ -1335,10 +1361,10 @@ CREATE TABLE $wpdb->signups (
|
|
1335 |
}
|
1336 |
|
1337 |
?>
|
1338 |
-
<div class="wrap">
|
1339 |
<h1><?php echo $updraftplus->plugin_title; ?></h1>
|
1340 |
|
1341 |
-
<?php _e('By UpdraftPlus.Com','updraftplus')?> ( <a href="http://updraftplus.com">UpdraftPlus.Com</a> | <a href="http://updraftplus.com/news/"><?php _e('News','updraftplus');?></a> | <?php if (!defined('
|
1342 |
<br>
|
1343 |
|
1344 |
<div id="updraft-hidethis">
|
@@ -1348,24 +1374,23 @@ CREATE TABLE $wpdb->signups (
|
|
1348 |
|
1349 |
<?php
|
1350 |
if(isset($_GET['updraft_restore_success'])) {
|
|
|
1351 |
echo "<div class=\"updated fade\" style=\"padding:8px;\"><strong>".__('Your backup has been restored.','updraftplus').'</strong> '.__('If your restore included files, then your old (themes, uploads, plugins, whatever) directories have been retained with "-old" appended to their name. Remove them when you are satisfied that the backup worked properly.')."</div>";
|
1352 |
}
|
1353 |
|
1354 |
$ws_advert = $updraftplus->wordshell_random_advert(1);
|
1355 |
if ($ws_advert) { echo '<div class="updated fade" style="max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;">'.$ws_advert.'</div>'; }
|
1356 |
|
1357 |
-
if($deleted_old_dirs) echo '<div style="color:blue" class=\"updated fade\">'.__('Old directories successfully deleted.','updraftplus').'</div>';
|
1358 |
-
|
1359 |
if(!$updraftplus->memory_check(64)) {?>
|
1360 |
-
<div class="updated
|
1361 |
<?php
|
1362 |
}
|
1363 |
if($this->scan_old_dirs()) {?>
|
1364 |
-
<div class="updated
|
1365 |
-
<form method="post" action="<?php echo remove_query_arg(array('updraft_restore_success','action')) ?>">
|
1366 |
-
<?php wp_nonce_field('
|
1367 |
<input type="hidden" name="action" value="updraft_delete_old_dirs" />
|
1368 |
-
<input type="submit" class="button-primary" value="<?php
|
1369 |
</form>
|
1370 |
</div>
|
1371 |
<?php
|
@@ -1443,7 +1468,7 @@ CREATE TABLE $wpdb->signups (
|
|
1443 |
|
1444 |
<div style="float:left; width:200px; margin-top: <?php echo (class_exists('UpdraftPlus_Addons_Migrator')) ? "20" : "0" ?>px;">
|
1445 |
<div style="margin-bottom: 10px;">
|
1446 |
-
<button type="button" <?php echo $backup_disabled ?> class="button-primary updraft-bigbutton" style="padding-top:2px;padding-bottom:2px;font-size:22px !important; min-height: 32px; min-width: 180px;" onclick="jQuery('#updraft-backupnow-modal').dialog('open');"><?php _e('Backup Now','updraftplus');?></button>
|
1447 |
</div>
|
1448 |
<div style="margin-bottom: 10px;">
|
1449 |
<?php
|
@@ -1535,6 +1560,11 @@ CREATE TABLE $wpdb->signups (
|
|
1535 |
</tr>
|
1536 |
</table>
|
1537 |
|
|
|
|
|
|
|
|
|
|
|
1538 |
<div id="updraft-delete-modal" title="<?php _e('Delete backup set', 'updraftplus');?>">
|
1539 |
<form id="updraft_delete_form" method="post">
|
1540 |
<p style="margin-top:3px; padding-top:0">
|
@@ -1641,7 +1671,8 @@ CREATE TABLE $wpdb->signups (
|
|
1641 |
|
1642 |
<p>
|
1643 |
<input type="checkbox" id="backupnow_nodb"> <label for="backupnow_nodb"><?php _e("Don't include the database in the backup", 'updraftplus'); ?></label><br>
|
1644 |
-
<input type="checkbox" id="backupnow_nofiles"> <label for="backupnow_nofiles"><?php _e("Don't include any files in the backup", 'updraftplus'); ?></label>
|
|
|
1645 |
</p>
|
1646 |
|
1647 |
<p><?php _e('Does nothing happen when you attempt backups?','updraftplus');?> <a href="http://updraftplus.com/faqs/my-scheduled-backups-and-pressing-backup-now-does-nothing-however-pressing-debug-backup-does-produce-a-backup/"><?php _e('Go here for help.', 'updraftplus');?></a></p>
|
@@ -1909,18 +1940,37 @@ CREATE TABLE $wpdb->signups (
|
|
1909 |
|
1910 |
}
|
1911 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1912 |
//deletes the -old directories that are created when a backup is restored.
|
1913 |
function delete_old_dirs() {
|
1914 |
-
global $wp_filesystem;
|
1915 |
-
$credentials = request_filesystem_credentials(wp_nonce_url(UpdraftPlus_Options::admin_page_url()."?page=updraftplus&action=updraft_delete_old_dirs", '
|
1916 |
WP_Filesystem($credentials);
|
1917 |
-
if (
|
1918 |
-
foreach (
|
1919 |
show_message($message);
|
1920 |
exit;
|
1921 |
}
|
1922 |
// From WP_CONTENT_DIR - which contains 'themes'
|
1923 |
$ret = $this->delete_old_dirs_dir($wp_filesystem->wp_content_dir());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1924 |
// $ret2 = $this->delete_old_dirs_dir($wp_filesystem->abspath());
|
1925 |
$plugs = untrailingslashit($wp_filesystem->wp_plugins_dir());
|
1926 |
if ($wp_filesystem->is_dir($plugs.'-old')) {
|
@@ -1936,25 +1986,43 @@ CREATE TABLE $wpdb->signups (
|
|
1936 |
$ret3 = true;
|
1937 |
}
|
1938 |
|
1939 |
-
return
|
1940 |
}
|
1941 |
|
1942 |
-
function delete_old_dirs_dir($dir) {
|
1943 |
|
1944 |
-
|
1945 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1946 |
if (!is_array($list)) return false;
|
1947 |
|
1948 |
$ret = true;
|
1949 |
foreach ($list as $item) {
|
1950 |
-
|
|
|
1951 |
//recursively delete
|
1952 |
-
print "<strong>".__('Delete','updraftplus').": </strong>".htmlspecialchars($
|
1953 |
-
|
1954 |
-
|
1955 |
-
|
|
|
|
|
|
|
|
|
|
|
1956 |
} else {
|
1957 |
-
|
|
|
|
|
|
|
|
|
|
|
1958 |
}
|
1959 |
}
|
1960 |
}
|
@@ -2017,13 +2085,14 @@ CREATE TABLE $wpdb->signups (
|
|
2017 |
|
2018 |
//scans the content dir to see if any -old dirs are present
|
2019 |
function scan_old_dirs() {
|
2020 |
-
$
|
2021 |
-
|
2022 |
-
|
2023 |
-
|
|
|
|
|
2024 |
# No need to scan ABSPATH - we don't backup there
|
2025 |
-
|
2026 |
-
if (is_dir($plugdir.'-old')) return true;
|
2027 |
return false;
|
2028 |
}
|
2029 |
|
@@ -2097,7 +2166,7 @@ CREATE TABLE $wpdb->signups (
|
|
2097 |
echo __('and retain this many backups', 'updraftplus').': ';
|
2098 |
$updraft_retain = (int)UpdraftPlus_Options::get_updraft_option('updraft_retain', 2);
|
2099 |
$updraft_retain = ($updraft_retain > 0) ? $updraft_retain : 1;
|
2100 |
-
?> <input type="
|
2101 |
</td>
|
2102 |
</tr>
|
2103 |
<tr>
|
@@ -2115,7 +2184,7 @@ CREATE TABLE $wpdb->signups (
|
|
2115 |
echo __('and retain this many backups', 'updraftplus').': ';
|
2116 |
$updraft_retain_db = (int)UpdraftPlus_Options::get_updraft_option('updraft_retain_db', $updraft_retain);
|
2117 |
$updraft_retain_db = ($updraft_retain_db > 0) ? $updraft_retain_db : 1;
|
2118 |
-
?> <input type="
|
2119 |
</td>
|
2120 |
</tr>
|
2121 |
<tr class="backup-interval-description">
|
@@ -2217,23 +2286,14 @@ CREATE TABLE $wpdb->signups (
|
|
2217 |
?>
|
2218 |
|
2219 |
<tr>
|
2220 |
-
<th><?php _e('Email','updraftplus'); ?>:</th>
|
2221 |
<td>
|
2222 |
<?php
|
2223 |
$updraft_email = UpdraftPlus_Options::get_updraft_option('updraft_email');
|
2224 |
?>
|
2225 |
-
<input type="
|
2226 |
<?php
|
2227 |
-
|
2228 |
-
if ($updraftplus->have_addons <10) {
|
2229 |
-
if ($updraft_email && $updraft_email != $admin_email) {
|
2230 |
-
echo '<strong>';
|
2231 |
-
}
|
2232 |
-
echo ' '.apply_filters('updraft_reportingemailnotice', sprintf(__("With the next release of UpdraftPlus, you will need an add-on to use a different email address to the site owner's (%s).", 'updraftplus'), $admin_email));
|
2233 |
-
if ($updraft_email && $updraft_email != $admin_email) {
|
2234 |
-
echo '</strong>';
|
2235 |
-
}
|
2236 |
-
}
|
2237 |
?>
|
2238 |
</td>
|
2239 |
</tr>
|
@@ -2283,7 +2343,7 @@ CREATE TABLE $wpdb->signups (
|
|
2283 |
foreach ($updraftplus->backup_methods as $method => $description) {
|
2284 |
do_action('updraftplus_config_print_before_storage', $method);
|
2285 |
require_once(UPDRAFTPLUS_DIR.'/methods/'.$method.'.php');
|
2286 |
-
$call_method =
|
2287 |
call_user_func(array($call_method, 'config_print'));
|
2288 |
do_action('updraftplus_config_print_after_storage', $method);
|
2289 |
}
|
@@ -2730,7 +2790,7 @@ ENDHERE;
|
|
2730 |
$backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
|
2731 |
if(!is_array($backup_history[$timestamp])) {
|
2732 |
echo '<p>'.__('This backup does not exist in the backup history - restoration aborted. Timestamp:','updraftplus')." $timestamp</p><br/>";
|
2733 |
-
return new WP_Error('does_not_exist', 'Backup does not exist in the backup history');
|
2734 |
}
|
2735 |
|
2736 |
// request_filesystem_credentials passes on fields just via hidden name/value pairs.
|
@@ -2759,6 +2819,7 @@ ENDHERE;
|
|
2759 |
$updraftplus->jobdata_set('job_type', 'restore');
|
2760 |
$updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms);
|
2761 |
$updraftplus->logfile_open($updraftplus->nonce);
|
|
|
2762 |
# TODO: Provide download link for the log file
|
2763 |
# TODO: Automatic purging of old log files
|
2764 |
# TODO: Provide option to auto-email the log file
|
@@ -2790,12 +2851,14 @@ ENDHERE;
|
|
2790 |
|
2791 |
$updraftplus->log("Restore job started. Entities to restore: $entities_log");
|
2792 |
|
2793 |
-
if (count($_POST['updraft_restore'])
|
2794 |
echo '<p>'.__('ABORT: Could not find the information on which entities to restore.', 'updraftplus').'</p>';
|
2795 |
echo '<p>'.__('If making a request for support, please include this information:','updraftplus').' '.count($_POST).' : '.htmlspecialchars(serialize($_POST)).'</p>';
|
2796 |
return new WP_Error('missing_info', 'Backup information not found');
|
2797 |
}
|
2798 |
|
|
|
|
|
2799 |
/*
|
2800 |
$_POST['updraft_restore'] is typically something like: array( 0=>'db', 1=>'plugins', 2=>'themes'), etc.
|
2801 |
i.e. array ( 'db', 'plugins', themes')
|
@@ -2810,7 +2873,7 @@ ENDHERE;
|
|
2810 |
require_once(UPDRAFTPLUS_DIR.'/restorer.php');
|
2811 |
|
2812 |
global $updraftplus_restorer;
|
2813 |
-
$updraftplus_restorer = new Updraft_Restorer();
|
2814 |
|
2815 |
$second_loop = array();
|
2816 |
|
@@ -2872,6 +2935,7 @@ ENDHERE;
|
|
2872 |
echo __('Could not find one of the files for restoration', 'updraftplus')." ($file)<br>";
|
2873 |
$updraftplus->log("$file: ".__('Could not find one of the files for restoration', 'updraftplus'), 'error');
|
2874 |
echo '</div>';
|
|
|
2875 |
return false;
|
2876 |
}
|
2877 |
}
|
@@ -2880,13 +2944,16 @@ ENDHERE;
|
|
2880 |
|
2881 |
$val = $updraftplus_restorer->pre_restore_backup($files, $type, $info);
|
2882 |
if (is_wp_error($val)) {
|
|
|
2883 |
foreach ($val->get_error_messages() as $msg) {
|
2884 |
echo '<strong>'.__('Error:', 'updraftplus').'</strong> '.htmlspecialchars($msg).'<br>';
|
2885 |
}
|
2886 |
echo '</div>'; //close the updraft_restore_progress div even if we error
|
|
|
2887 |
return $val;
|
2888 |
} elseif (false === $val) {
|
2889 |
echo '</div>'; //close the updraft_restore_progress div even if we error
|
|
|
2890 |
return false;
|
2891 |
}
|
2892 |
|
@@ -2894,7 +2961,7 @@ ENDHERE;
|
|
2894 |
}
|
2895 |
$updraftplus_restorer->delete = (UpdraftPlus_Options::get_updraft_option('updraft_delete_local')) ? true : false;
|
2896 |
if ('none' === $service || empty($service) || (is_array($service) && 1 == count($service) && (in_array('none', $service) || in_array('', $service)))) {
|
2897 |
-
if ($updraftplus_restorer->delete)
|
2898 |
$updraftplus_restorer->delete = false;
|
2899 |
}
|
2900 |
|
@@ -2905,40 +2972,59 @@ ENDHERE;
|
|
2905 |
$info = (isset($backupable_entities[$type])) ? $backupable_entities[$type] : array();
|
2906 |
|
2907 |
echo ('db' == $type) ? "<h2>".__('Database','updraftplus')."</h2>" : "<h2>".$info['description']."</h2>";
|
2908 |
-
|
2909 |
|
2910 |
if (is_string($files)) $files = array($files);
|
2911 |
foreach ($files as $file) {
|
2912 |
$val = $updraftplus_restorer->restore_backup($file, $type, $info);
|
2913 |
|
2914 |
if(is_wp_error($val)) {
|
|
|
2915 |
foreach ($val->get_error_messages() as $msg) {
|
2916 |
echo '<strong>'.__('Error message', 'updraftplus').':</strong> '.htmlspecialchars($msg).'<br>';
|
2917 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2918 |
echo '</div>'; //close the updraft_restore_progress div even if we error
|
|
|
2919 |
return $val;
|
2920 |
} elseif (false === $val) {
|
2921 |
echo '</div>'; //close the updraft_restore_progress div even if we error
|
|
|
2922 |
return false;
|
2923 |
}
|
2924 |
}
|
2925 |
}
|
2926 |
|
2927 |
echo '</div>'; //close the updraft_restore_progress div
|
|
|
|
|
2928 |
return true;
|
2929 |
}
|
2930 |
|
2931 |
function sort_restoration_entities($a, $b) {
|
2932 |
if ($a == $b) return 0;
|
2933 |
# Put the database first
|
2934 |
-
if (
|
2935 |
-
if (
|
2936 |
return strcmp($a, $b);
|
2937 |
}
|
2938 |
|
|
|
|
|
|
|
|
|
|
|
2939 |
function get_settings_keys() {
|
2940 |
-
return array('updraft_autobackup_default', 'updraftplus_tmp_googledrive_access_token', 'updraftplus_dismissedautobackup', 'updraft_interval', 'updraft_interval_database', 'updraft_retain', 'updraft_retain_db', 'updraft_encryptionphrase', 'updraft_service', 'updraft_dropbox_appkey', 'updraft_dropbox_secret', 'updraft_googledrive_clientid', 'updraft_googledrive_secret', 'updraft_googledrive_remotepath', 'updraft_ftp_login', 'updraft_ftp_pass', 'updraft_ftp_remote_path', 'updraft_server_address', 'updraft_dir', 'updraft_email', 'updraft_delete_local', 'updraft_debug_mode', 'updraft_include_plugins', 'updraft_include_themes', 'updraft_include_uploads', 'updraft_include_others', 'updraft_include_wpcore', 'updraft_include_wpcore_exclude', 'updraft_include_more',
|
2941 |
-
|
2942 |
}
|
2943 |
|
2944 |
}
|
74 |
|
75 |
if (version_compare($wp_version, '3.2', '<')) add_action('all_admin_notices', array($this, 'show_admin_warning_wordpressversion'));
|
76 |
|
77 |
+
wp_enqueue_script('updraftplus-admin-ui', UPDRAFTPLUS_URL.'/includes/updraft-admin-ui.js', array('jquery', 'jquery-ui-dialog', 'plupload-all'), '30');
|
78 |
|
79 |
wp_localize_script( 'updraftplus-admin-ui', 'updraftlion', array(
|
80 |
'sendonlyonwarnings' => __('Send a report only when there are warnings/errors', 'updraftplus'),
|
81 |
+
'wholebackup' => __('When the Email storage method is enabled, also send the entire backup', 'updraftplus'),
|
82 |
+
'emailsizelimits' => esc_attr(sprintf(__('Be aware that mail servers tend to have size limits; typically around %s Mb; backups larger than any limits will likely not arrive.','updraftplus'), '10-20')),
|
83 |
'rescanning' => __('Rescanning (looking for backups that you have uploaded manually into the internal backup store)...','updraftplus'),
|
84 |
+
'enteremailhere' => esc_attr(__('To send to more than one address, separate each address with a comma.', 'updraftplus')),
|
85 |
'excludedeverything' => __('If you exclude both the database and the files, then you have excluded everything!', 'updraftplus'),
|
86 |
'restoreproceeding' => __('The restore operation has begun. Do not press stop or close your browser until it reports itself as having finished.', 'updraftplus'),
|
87 |
'unexpectedresponse' => __('Unexpected response:','updraftplus'),
|
88 |
'servererrorcode' => __('The web server returned an error code (try again, or check your web server logs)', 'updraftplus'),
|
89 |
+
'newuserpass' => __("The new user's RackSpace console password is (this will not be shown again):", 'updraftplus'),
|
90 |
+
'trying' => __('Trying...', 'updraftplus'),
|
91 |
'calculating' => __('calculating...','updraftplus'),
|
92 |
'begunlooking' => __('Begun looking for this entity','updraftplus'),
|
93 |
'stilldownloading' => __('Some files are still downloading or being processed - please wait.', 'updraftplus'),
|
105 |
'notunderstood' => __('Download error: the server sent us a response which we did not understand.', 'updraftplus'),
|
106 |
'requeststart' => __('Requesting start of backup...', 'updraftplus'),
|
107 |
'phpinfo' => __('PHP information', 'updraftplus'),
|
108 |
+
'delete_old_dirs' => __('Delete Old Directories', 'updraftplus'),
|
109 |
'raw' => __('Raw backup history', 'updraftplus'),
|
110 |
'notarchive' => __('This file does not appear to be an UpdraftPlus backup archive (such files are .zip or .gz files which have a name like: backup_(time)_(site name)_(code)_(type).(zip|gz)). However, UpdraftPlus archives are standard zip/SQL files - so if you are sure that your file has the right format, then you can rename it to match that pattern.','updraftplus'),
|
111 |
'makesure' => __('(make sure that you were trying to upload a zip file previously created by UpdraftPlus)','updraftplus'),
|
120 |
'backupnow' => __('Backup Now', 'updraftplus'),
|
121 |
'cancel' => __('Cancel', 'updraftplus'),
|
122 |
'deletebutton' => __('Delete', 'updraftplus'),
|
123 |
+
'createbutton' => __('Create', 'updraftplus'),
|
124 |
'close' => __('Close', 'updraftplus'),
|
125 |
'restore' => __('Restore', 'updraftplus'),
|
126 |
) );
|
129 |
|
130 |
function core_upgrade_preamble() {
|
131 |
if (!class_exists('UpdraftPlus_Addon_Autobackup')) {
|
132 |
+
if (defined('UPDRAFTPLUS_NOADS_A')) return;
|
133 |
# TODO: Remove legacy/wrong use of transient any time from 1 Jun 2014
|
134 |
if (true == get_transient('updraftplus_dismissedautobackup')) return;
|
135 |
$dismissed_until = UpdraftPlus_Options::get_updraft_option('updraftplus_dismissedautobackup', 0);
|
196 |
var updraft_downloader_nonce = '<?php wp_create_nonce("updraftplus_download"); ?>'
|
197 |
</script>
|
198 |
<style type="text/css">
|
199 |
+
#updraft-wrap .form-table th {
|
200 |
+
width: 230px;
|
201 |
+
}
|
202 |
.updraftplus-remove a {
|
203 |
color: red;
|
204 |
}
|
269 |
|
270 |
function admin_action_upgrade_pluginortheme() {
|
271 |
|
272 |
+
if (isset($_GET['action']) && ($_GET['action'] == 'upgrade-plugin' || $_GET['action'] == 'upgrade-theme') && !class_exists('UpdraftPlus_Addon_Autobackup') && !defined('UPDRAFTPLUS_NOADS_A')) {
|
273 |
|
274 |
# TODO: Remove legacy/erroneous use of transient any time after 1 Jun 2014
|
275 |
$dismissed = get_transient('updraftplus_dismissedautobackup');
|
373 |
if ($_REQUEST['type'] == $type) $type_match = true;
|
374 |
}
|
375 |
|
376 |
+
if (!$type_match && 'db' != $_REQUEST['type']) exit;
|
377 |
|
378 |
// Get the information on what is wanted
|
379 |
$type = $_REQUEST['type'];
|
415 |
// Note that log() assumes that the data is in _POST, not _GET
|
416 |
if ($debug_mode) $updraftplus->logfile_open($updraftplus->nonce);
|
417 |
|
418 |
+
set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT);
|
419 |
+
|
420 |
$updraftplus->log("Requested to obtain file: timestamp=$timestamp, type=$type, index=$findex");
|
421 |
|
422 |
$itext = (empty($findex)) ? '' : $findex;
|
425 |
$services = (isset($backup_history[$timestamp]['service'])) ? $backup_history[$timestamp]['service'] : false;
|
426 |
if (is_string($services)) $services = array($services);
|
427 |
|
428 |
+
$updraftplus->jobdata_set('service', $services);
|
429 |
|
430 |
// Fetch it from the cloud, if we have not already got it
|
431 |
|
458 |
$is_downloaded = false;
|
459 |
foreach ($services as $service) {
|
460 |
if ($is_downloaded) continue;
|
461 |
+
$download = $this->download_file($file, $service);
|
462 |
+
if (is_readable($fullpath) && $download !== false) {
|
463 |
clearstatcache();
|
464 |
$updraftplus->log('Remote fetch was successful (file size: '.round(filesize($fullpath)/1024,1).' Kb)');
|
465 |
$is_downloaded = true;
|
466 |
} else {
|
467 |
+
clearstatcache();
|
468 |
+
if (0 === @filesize($fullpath)) @unlink($fullpath);
|
469 |
$updraftplus->log('Remote fetch failed');
|
470 |
}
|
471 |
}
|
483 |
$updraftplus->log('Remote fetch failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then remote retrieval may have failed.');
|
484 |
}
|
485 |
|
486 |
+
restore_error_handler();
|
487 |
+
|
488 |
@fclose($updraftplus->logfile_handle);
|
489 |
+
if (!$debug_mode) @unlink($updraftplus->logfile_name);
|
490 |
|
491 |
exit;
|
492 |
|
507 |
$objname = "UpdraftPlus_BackupModule_${service}";
|
508 |
if (method_exists($objname, "download")) {
|
509 |
$remote_obj = new $objname;
|
510 |
+
return $remote_obj->download($file);
|
511 |
} else {
|
512 |
$updraftplus->log("Automatic backup restoration is not available with the method: $service.");
|
513 |
$updraftplus->log("$file: ".sprintf(__("The backup archive for this file could not be found. The remote storage method in use (%s) does not allow us to retrieve files. To perform any restoration using UpdraftPlus, you will need to obtain a copy of this file and place it inside UpdraftPlus's working folder", 'updraftplus'), $service)." (".$this->prune_updraft_dir_prefix($updraftplus->backups_dir_location()).")", 'error');
|
514 |
+
return false;
|
515 |
}
|
516 |
|
517 |
}
|
651 |
$updraftplus->jobdata_set('job_type', 'delete');
|
652 |
$updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms);
|
653 |
|
654 |
+
if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) {
|
655 |
+
$updraftplus->logfile_open($updraftplus->nonce);
|
656 |
+
set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT);
|
657 |
+
}
|
658 |
|
659 |
$updraft_dir = $updraftplus->backups_dir_location();
|
660 |
$backupable_entities = $updraftplus->get_backupable_file_entities(true, true);
|
734 |
|
735 |
print json_encode(array('result' => 'success', 'message' => $message));
|
736 |
|
737 |
+
if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode')) {
|
738 |
+
restore_error_handler();
|
739 |
+
}
|
740 |
+
|
741 |
+
|
742 |
} elseif ('rawbackuphistory' == $_REQUEST['subaction']) {
|
743 |
echo '<h3>'.__('Known backups (raw)', 'updraftplus').'</h3><pre>';
|
744 |
var_dump($updraftplus->get_backup_history());
|
785 |
} elseif ('ping' == $_REQUEST['subaction']) {
|
786 |
// The purpose of this is to detect brokenness caused by extra line feeds in plugins/themes - before it breaks other AJAX operations and leads to support requests
|
787 |
echo 'pong';
|
788 |
+
} elseif ('delete_old_dirs' == $_REQUEST['subaction']) {
|
789 |
+
$this->delete_old_dirs_go(false);
|
790 |
} elseif ('phpinfo' == $_REQUEST['subaction']) {
|
791 |
phpinfo(INFO_ALL ^ (INFO_CREDITS | INFO_LICENSE));
|
792 |
+
} elseif ('doaction' == $_REQUEST['subaction'] && !empty($_REQUEST['subsubaction']) && 'updraft_' == substr($_REQUEST['subsubaction'], 0, 8)) {
|
793 |
+
do_action($_REQUEST['subsubaction']);
|
794 |
} elseif ('backupnow' == $_REQUEST['subaction']) {
|
795 |
echo '<strong>',__('Schedule backup','updraftplus').':</strong> ';
|
796 |
+
$backupnow_nocloud = (empty($_REQUEST['backupnow_nocloud'])) ? false : true;
|
797 |
$event = (!empty($_REQUEST['backupnow_nofiles'])) ? 'updraft_backupnow_backup_database' : ((!empty($_REQUEST['backupnow_nodb'])) ? 'updraft_backupnow_backup' : 'updraft_backupnow_backup_all');
|
798 |
+
if (wp_schedule_single_event(time()+5, $event, array($backupnow_nocloud)) === false) {
|
799 |
$updraftplus->log("A backup run failed to schedule");
|
800 |
echo __("Failed.",'updraftplus')."</div>";
|
801 |
} else {
|
924 |
|
925 |
$db_file = (is_string($backup['db'])) ? $updraft_dir.'/'.$backup['db'] : $updraft_dir.'/'.$backup['db'][0];
|
926 |
|
927 |
+
if (!is_readable($db_file)) return array($mess, $warn, $err);
|
928 |
|
929 |
// Encrypted - decrypt it
|
930 |
if ($updraftplus->is_db_encrypted($db_file)) {
|
981 |
|
982 |
$migration_warning = false;
|
983 |
|
984 |
+
# Don't set too high - we want a timely response returned to the browser
|
985 |
+
@set_time_limit(90);
|
986 |
+
|
987 |
while (!gzeof($dbhandle) && ($line<100 || count($wanted_tables)>0)) {
|
988 |
$line++;
|
989 |
// Up to 1Mb
|
990 |
$buffer = rtrim(gzgets($dbhandle, 1048576));
|
991 |
// Comments are what we are interested in
|
992 |
if (substr($buffer, 0, 1) == '#') {
|
|
|
993 |
if ('' == $old_siteurl && preg_match('/^\# Backup of: (http(.*))$/', $buffer, $matches)) {
|
994 |
+
$old_siteurl = untrailingslashit($matches[1]);
|
995 |
$mess[] = __('Backup of:', 'updraftplus').' '.htmlspecialchars($old_siteurl).((!empty($old_wp_version)) ? ' '.sprintf(__('(version: %s)', 'updraftplus'), $old_wp_version) : '');
|
996 |
// Check for should-be migration
|
997 |
+
if (!$migration_warning && $old_siteurl != untrailingslashit(site_url())) {
|
998 |
$migration_warning = true;
|
999 |
$powarn = apply_filters('updraftplus_dbscan_urlchange', sprintf(__('Warning: %s', 'updraftplus'), '<a href="http://updraftplus.com/shop/migrator/">'.__('This backup set is from a different site - this is not a restoration, but a migration. You need the Migrator add-on in order to make this work.', 'updraftplus').'</a>'), $old_siteurl, $res);
|
1000 |
if (!empty($powarn)) $warn[] = $powarn;
|
1001 |
}
|
1002 |
} elseif ('' == $old_home && preg_match('/^\# Home URL: (http(.*))$/', $buffer, $matches)) {
|
1003 |
+
$old_home = untrailingslashit($matches[1]);
|
1004 |
// Check for should-be migration
|
1005 |
if (!$migration_warning && $old_home != home_url()) {
|
1006 |
$migration_warning = true;
|
1296 |
// If we restored the database, then that will have out-of-date information which may confuse the user - so automatically re-scan for them.
|
1297 |
$this->rebuild_backup_history();
|
1298 |
echo '<p><strong>'.__('Restore successful!','updraftplus').'</strong></p>';
|
1299 |
+
$updraftplus->log("Restore successful");
|
1300 |
echo '<b>'.__('Actions','updraftplus').':</b> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&updraft_restore_success=true">'.__('Return to UpdraftPlus Configuration','updraftplus').'</a>';
|
1301 |
return;
|
1302 |
} elseif (is_wp_error($backup_success)) {
|
1303 |
echo '<p>Restore failed...</p>';
|
1304 |
+
$updraftplus->log_wp_error($backup_success);
|
1305 |
+
$updraftplus->log("Restore failed");
|
1306 |
$updraftplus->list_errors();
|
1307 |
echo '<b>Actions:</b> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus">'.__('Return to UpdraftPlus Configuration','updraftplus').'</a>';
|
1308 |
return;
|
1311 |
return;
|
1312 |
}
|
1313 |
}
|
|
|
|
|
|
|
|
|
1314 |
|
1315 |
+
if(isset($_REQUEST['action']) && 'updraft_delete_old_dirs' == $_REQUEST['action']) {
|
1316 |
$nonce = (empty($_REQUEST['_wpnonce'])) ? "" : $_REQUEST['_wpnonce'];
|
1317 |
+
if (!wp_verify_nonce($nonce, 'updraftplus-credentialtest-nonce')) die('Security check');
|
1318 |
+
$this->delete_old_dirs_go();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1319 |
return;
|
1320 |
}
|
1321 |
|
1338 |
return;
|
1339 |
}
|
1340 |
|
1341 |
+
do_action('updraftplus_settings_page_init');
|
1342 |
+
|
1343 |
echo '<div id="updraft_backup_started" class="updated fade" style="display:none; max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;"></div>';
|
1344 |
|
1345 |
// updraft_file_ids is not deleted
|
1361 |
}
|
1362 |
|
1363 |
?>
|
1364 |
+
<div class="wrap" id="updraft-wrap">
|
1365 |
<h1><?php echo $updraftplus->plugin_title; ?></h1>
|
1366 |
|
1367 |
+
<?php _e('By UpdraftPlus.Com','updraftplus')?> ( <a href="http://updraftplus.com">UpdraftPlus.Com</a> | <a href="http://updraftplus.com/news/"><?php _e('News','updraftplus');?></a> | <?php if (!defined('UPDRAFTPLUS_NOADS_A')) { ?><a href="http://updraftplus.com/shop/"><?php _e("Premium",'updraftplus');?></a> | <?php } ?><a href="http://updraftplus.com/support/"><?php _e("Support",'updraftplus');?></a> | <a href="http://david.dw-perspective.org.uk"><?php _e("Lead developer's homepage",'updraftplus');?></a> | <?php if (1==0 && !defined('UPDRAFTPLUS_NOADS_A')) { ?><a href="http://wordshell.net">WordShell - WordPress command line</a> | <a href="http://david.dw-perspective.org.uk/donate"><?php _e('Donate','updraftplus');?></a> | <?php } ?><a href="http://updraftplus.com/support/frequently-asked-questions/">FAQs</a> | <a href="http://profiles.wordpress.org/davidanderson/"><?php _e('More plugins','updraftplus');?></a> ) <?php _e('Version','updraftplus');?>: <?php echo $updraftplus->version; ?>
|
1368 |
<br>
|
1369 |
|
1370 |
<div id="updraft-hidethis">
|
1374 |
|
1375 |
<?php
|
1376 |
if(isset($_GET['updraft_restore_success'])) {
|
1377 |
+
|
1378 |
echo "<div class=\"updated fade\" style=\"padding:8px;\"><strong>".__('Your backup has been restored.','updraftplus').'</strong> '.__('If your restore included files, then your old (themes, uploads, plugins, whatever) directories have been retained with "-old" appended to their name. Remove them when you are satisfied that the backup worked properly.')."</div>";
|
1379 |
}
|
1380 |
|
1381 |
$ws_advert = $updraftplus->wordshell_random_advert(1);
|
1382 |
if ($ws_advert) { echo '<div class="updated fade" style="max-width: 800px; font-size:140%; line-height: 140%; padding:14px; clear:left;">'.$ws_advert.'</div>'; }
|
1383 |
|
|
|
|
|
1384 |
if(!$updraftplus->memory_check(64)) {?>
|
1385 |
+
<div class="updated" style="padding:8px;"><?php _e("Your PHP memory limit (set by your web hosting company) is very low. UpdraftPlus attempted to raise it but was unsuccessful. This plugin may struggle with a memory limit of less than 64 Mb - especially if you have very large files uploaded (though on the other hand, many sites will be successful with a 32Mb limit - your experience may vary).",'updraftplus');?> <?php _e('Current limit is:','updraftplus');?> <?php echo $updraftplus->memory_check_current(); ?> Mb</div>
|
1386 |
<?php
|
1387 |
}
|
1388 |
if($this->scan_old_dirs()) {?>
|
1389 |
+
<div id="updraft_delete_old_dirs_pagediv" class="updated" style="padding:8px;"><p><?php _e('Your WordPress install has old directories from its state before you restored/migrated (technical information: these are suffixed with -old). You should press this button to delete them as soon as you have verified that the restoration worked.','updraftplus');?></p>
|
1390 |
+
<form method="post" onsubmit="return updraft_delete_old_dirs();" action="<?php echo remove_query_arg(array('updraft_restore_success','action')) ?>">
|
1391 |
+
<?php wp_nonce_field('updraftplus-credentialtest-nonce'); ?>
|
1392 |
<input type="hidden" name="action" value="updraft_delete_old_dirs" />
|
1393 |
+
<input type="submit" class="button-primary" value="<?php echo esc_attr(__('Delete Old Directories', 'updraftplus'));?>" />
|
1394 |
</form>
|
1395 |
</div>
|
1396 |
<?php
|
1468 |
|
1469 |
<div style="float:left; width:200px; margin-top: <?php echo (class_exists('UpdraftPlus_Addons_Migrator')) ? "20" : "0" ?>px;">
|
1470 |
<div style="margin-bottom: 10px;">
|
1471 |
+
<button type="button" <?php echo $backup_disabled ?> class="button-primary updraft-bigbutton" style="padding-top:2px;padding-bottom:2px;font-size:22px !important; min-height: 32px; min-width: 180px;" <?php if ($backup_disabled) echo 'title="'.esc_attr(__('This button is disabled because your backup directory is not writable (see the setting futher down the page).', 'updraftplus')).'" ';?> onclick="jQuery('#updraft-backupnow-modal').dialog('open');"><?php _e('Backup Now', 'updraftplus');?></button>
|
1472 |
</div>
|
1473 |
<div style="margin-bottom: 10px;">
|
1474 |
<?php
|
1560 |
</tr>
|
1561 |
</table>
|
1562 |
|
1563 |
+
<div id="updraft-message-modal" title="UpdraftPlus">
|
1564 |
+
<div id="updraft-message-modal-innards" style="font-size:115%; padding: 4px;">
|
1565 |
+
</div>
|
1566 |
+
</div>
|
1567 |
+
|
1568 |
<div id="updraft-delete-modal" title="<?php _e('Delete backup set', 'updraftplus');?>">
|
1569 |
<form id="updraft_delete_form" method="post">
|
1570 |
<p style="margin-top:3px; padding-top:0">
|
1671 |
|
1672 |
<p>
|
1673 |
<input type="checkbox" id="backupnow_nodb"> <label for="backupnow_nodb"><?php _e("Don't include the database in the backup", 'updraftplus'); ?></label><br>
|
1674 |
+
<input type="checkbox" id="backupnow_nofiles"> <label for="backupnow_nofiles"><?php _e("Don't include any files in the backup", 'updraftplus'); ?></label><br>
|
1675 |
+
<input type="checkbox" id="backupnow_nocloud"> <label for="backupnow_nocloud"><?php _e("Don't send this backup to cloud storage", 'updraftplus'); ?></label>
|
1676 |
</p>
|
1677 |
|
1678 |
<p><?php _e('Does nothing happen when you attempt backups?','updraftplus');?> <a href="http://updraftplus.com/faqs/my-scheduled-backups-and-pressing-backup-now-does-nothing-however-pressing-debug-backup-does-produce-a-backup/"><?php _e('Go here for help.', 'updraftplus');?></a></p>
|
1940 |
|
1941 |
}
|
1942 |
|
1943 |
+
function delete_old_dirs_go($show_return = true) {
|
1944 |
+
echo ($show_return) ? '<h1>UpdraftPlus - '.__('Remove old directories', 'updraftplus').'</h1>' : '<h2>'.__('Remove old directories', 'updraftplus').'</h2>';
|
1945 |
+
|
1946 |
+
if($this->delete_old_dirs()) {
|
1947 |
+
echo '<p>'.__('Old directories successfully removed.','updraftplus').'</p><br/>';
|
1948 |
+
} else {
|
1949 |
+
echo '<p>',__('Old directory removal failed for some reason. You may want to do this manually.','updraftplus').'</p><br/>';
|
1950 |
+
}
|
1951 |
+
if ($show_return) echo '<b>'.__('Actions','updraftplus').':</b> <a href="'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus">'.__('Return to UpdraftPlus Configuration','updraftplus').'</a>';
|
1952 |
+
}
|
1953 |
+
|
1954 |
//deletes the -old directories that are created when a backup is restored.
|
1955 |
function delete_old_dirs() {
|
1956 |
+
global $wp_filesystem, $updraftplus;
|
1957 |
+
$credentials = request_filesystem_credentials(wp_nonce_url(UpdraftPlus_Options::admin_page_url()."?page=updraftplus&action=updraft_delete_old_dirs", 'updraftplus-credentialtest-nonce'));
|
1958 |
WP_Filesystem($credentials);
|
1959 |
+
if ($wp_filesystem->errors->get_error_code()) {
|
1960 |
+
foreach ($wp_filesystem->errors->get_error_messages() as $message)
|
1961 |
show_message($message);
|
1962 |
exit;
|
1963 |
}
|
1964 |
// From WP_CONTENT_DIR - which contains 'themes'
|
1965 |
$ret = $this->delete_old_dirs_dir($wp_filesystem->wp_content_dir());
|
1966 |
+
|
1967 |
+
$updraft_dir = $updraftplus->backups_dir_location();
|
1968 |
+
if ($updraft_dir) {
|
1969 |
+
$ret4 = ($updraft_dir) ? $this->delete_old_dirs_dir($updraft_dir, false) : true;
|
1970 |
+
} else {
|
1971 |
+
$ret4 = true;
|
1972 |
+
}
|
1973 |
+
|
1974 |
// $ret2 = $this->delete_old_dirs_dir($wp_filesystem->abspath());
|
1975 |
$plugs = untrailingslashit($wp_filesystem->wp_plugins_dir());
|
1976 |
if ($wp_filesystem->is_dir($plugs.'-old')) {
|
1986 |
$ret3 = true;
|
1987 |
}
|
1988 |
|
1989 |
+
return $ret && $ret3 && $ret4;
|
1990 |
}
|
1991 |
|
1992 |
+
function delete_old_dirs_dir($dir, $wpfs = true) {
|
1993 |
|
1994 |
+
$dir = trailingslashit($dir);
|
1995 |
+
|
1996 |
+
global $wp_filesystem, $updraftplus;
|
1997 |
+
|
1998 |
+
if ($wpfs) {
|
1999 |
+
$list = $wp_filesystem->dirlist($dir);
|
2000 |
+
} else {
|
2001 |
+
$list = scandir($dir);
|
2002 |
+
}
|
2003 |
if (!is_array($list)) return false;
|
2004 |
|
2005 |
$ret = true;
|
2006 |
foreach ($list as $item) {
|
2007 |
+
$name = (is_array($item)) ? $item['name'] : $item;
|
2008 |
+
if ("-old" == substr($name, -4, 4)) {
|
2009 |
//recursively delete
|
2010 |
+
print "<strong>".__('Delete','updraftplus').": </strong>".htmlspecialchars($name).": ";
|
2011 |
+
|
2012 |
+
if ($wpfs) {
|
2013 |
+
if(!$wp_filesystem->delete($dir.$name, true)) {
|
2014 |
+
$ret = false;
|
2015 |
+
echo "<strong>".__('Failed', 'updraftplus')."</strong><br>";
|
2016 |
+
} else {
|
2017 |
+
echo "<strong>".__('OK', 'updraftplus')."</strong><br>";
|
2018 |
+
}
|
2019 |
} else {
|
2020 |
+
if ($updraftplus->remove_local_directory($dir.$name)) {
|
2021 |
+
echo "<strong>".__('OK', 'updraftplus')."</strong><br>";
|
2022 |
+
} else {
|
2023 |
+
$ret = false;
|
2024 |
+
echo "<strong>".__('Failed', 'updraftplus')."</strong><br>";
|
2025 |
+
}
|
2026 |
}
|
2027 |
}
|
2028 |
}
|
2085 |
|
2086 |
//scans the content dir to see if any -old dirs are present
|
2087 |
function scan_old_dirs() {
|
2088 |
+
global $updraftplus;
|
2089 |
+
$dirs = scandir(untrailingslashit(WP_CONTENT_DIR));
|
2090 |
+
if (!is_array($dirs)) $dirs = array();
|
2091 |
+
$dirs_u = scandir($updraftplus->backups_dir_location());
|
2092 |
+
if (!is_array($dirs_u)) $dirs_u = array();
|
2093 |
+
foreach (array_merge($dirs, $dirs_u) as $dir) { if (preg_match('/-old$/', $dir)) return true; }
|
2094 |
# No need to scan ABSPATH - we don't backup there
|
2095 |
+
if (is_dir(untrailingslashit(WP_PLUGIN_DIR).'-old')) return true;
|
|
|
2096 |
return false;
|
2097 |
}
|
2098 |
|
2166 |
echo __('and retain this many backups', 'updraftplus').': ';
|
2167 |
$updraft_retain = (int)UpdraftPlus_Options::get_updraft_option('updraft_retain', 2);
|
2168 |
$updraft_retain = ($updraft_retain > 0) ? $updraft_retain : 1;
|
2169 |
+
?> <input type="number" min="1" step="1" name="updraft_retain" value="<?php echo $updraft_retain ?>" style="width:40px;" />
|
2170 |
</td>
|
2171 |
</tr>
|
2172 |
<tr>
|
2184 |
echo __('and retain this many backups', 'updraftplus').': ';
|
2185 |
$updraft_retain_db = (int)UpdraftPlus_Options::get_updraft_option('updraft_retain_db', $updraft_retain);
|
2186 |
$updraft_retain_db = ($updraft_retain_db > 0) ? $updraft_retain_db : 1;
|
2187 |
+
?> <input type="number" min="1" step="1" name="updraft_retain_db" value="<?php echo $updraft_retain_db ?>" style="width:40px" />
|
2188 |
</td>
|
2189 |
</tr>
|
2190 |
<tr class="backup-interval-description">
|
2286 |
?>
|
2287 |
|
2288 |
<tr>
|
2289 |
+
<th><?php _e('Email', 'updraftplus'); ?>:</th>
|
2290 |
<td>
|
2291 |
<?php
|
2292 |
$updraft_email = UpdraftPlus_Options::get_updraft_option('updraft_email');
|
2293 |
?>
|
2294 |
+
<input type="checkbox" name="updraft_email" value="<?php esc_attr_e(get_bloginfo('admin_email')); ?>"<?php if (!empty($updraft_email)) echo ' checked="checked"';?> > <br><?php echo sprintf(__("Check this box to have a basic report sent to your site's admin address (%s).",'updraftplus'), htmlspecialchars(get_bloginfo('admin_email'))); ?>
|
2295 |
<?php
|
2296 |
+
if (!class_exists('UpdraftPlus_Addon_Reporting')) echo '<a href="http://updraftplus.com/shop/reporting/">'.__('For more reporting features, use the Reporting add-on.', 'updraftplus').'</a>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2297 |
?>
|
2298 |
</td>
|
2299 |
</tr>
|
2343 |
foreach ($updraftplus->backup_methods as $method => $description) {
|
2344 |
do_action('updraftplus_config_print_before_storage', $method);
|
2345 |
require_once(UPDRAFTPLUS_DIR.'/methods/'.$method.'.php');
|
2346 |
+
$call_method = 'UpdraftPlus_BackupModule_'.$method;
|
2347 |
call_user_func(array($call_method, 'config_print'));
|
2348 |
do_action('updraftplus_config_print_after_storage', $method);
|
2349 |
}
|
2790 |
$backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
|
2791 |
if(!is_array($backup_history[$timestamp])) {
|
2792 |
echo '<p>'.__('This backup does not exist in the backup history - restoration aborted. Timestamp:','updraftplus')." $timestamp</p><br/>";
|
2793 |
+
return new WP_Error('does_not_exist', __('Backup does not exist in the backup history', 'updraftplus'));
|
2794 |
}
|
2795 |
|
2796 |
// request_filesystem_credentials passes on fields just via hidden name/value pairs.
|
2819 |
$updraftplus->jobdata_set('job_type', 'restore');
|
2820 |
$updraftplus->jobdata_set('job_time_ms', $updraftplus->job_time_ms);
|
2821 |
$updraftplus->logfile_open($updraftplus->nonce);
|
2822 |
+
|
2823 |
# TODO: Provide download link for the log file
|
2824 |
# TODO: Automatic purging of old log files
|
2825 |
# TODO: Provide option to auto-email the log file
|
2851 |
|
2852 |
$updraftplus->log("Restore job started. Entities to restore: $entities_log");
|
2853 |
|
2854 |
+
if (0 == count($_POST['updraft_restore'])) {
|
2855 |
echo '<p>'.__('ABORT: Could not find the information on which entities to restore.', 'updraftplus').'</p>';
|
2856 |
echo '<p>'.__('If making a request for support, please include this information:','updraftplus').' '.count($_POST).' : '.htmlspecialchars(serialize($_POST)).'</p>';
|
2857 |
return new WP_Error('missing_info', 'Backup information not found');
|
2858 |
}
|
2859 |
|
2860 |
+
set_error_handler(array($updraftplus, 'php_error'), E_ALL & ~E_STRICT);
|
2861 |
+
|
2862 |
/*
|
2863 |
$_POST['updraft_restore'] is typically something like: array( 0=>'db', 1=>'plugins', 2=>'themes'), etc.
|
2864 |
i.e. array ( 'db', 'plugins', themes')
|
2873 |
require_once(UPDRAFTPLUS_DIR.'/restorer.php');
|
2874 |
|
2875 |
global $updraftplus_restorer;
|
2876 |
+
$updraftplus_restorer = new Updraft_Restorer(new Updraft_Restorer_Skin);
|
2877 |
|
2878 |
$second_loop = array();
|
2879 |
|
2935 |
echo __('Could not find one of the files for restoration', 'updraftplus')." ($file)<br>";
|
2936 |
$updraftplus->log("$file: ".__('Could not find one of the files for restoration', 'updraftplus'), 'error');
|
2937 |
echo '</div>';
|
2938 |
+
restore_error_handler();
|
2939 |
return false;
|
2940 |
}
|
2941 |
}
|
2944 |
|
2945 |
$val = $updraftplus_restorer->pre_restore_backup($files, $type, $info);
|
2946 |
if (is_wp_error($val)) {
|
2947 |
+
$updraftplus->log_wp_error($val);
|
2948 |
foreach ($val->get_error_messages() as $msg) {
|
2949 |
echo '<strong>'.__('Error:', 'updraftplus').'</strong> '.htmlspecialchars($msg).'<br>';
|
2950 |
}
|
2951 |
echo '</div>'; //close the updraft_restore_progress div even if we error
|
2952 |
+
restore_error_handler();
|
2953 |
return $val;
|
2954 |
} elseif (false === $val) {
|
2955 |
echo '</div>'; //close the updraft_restore_progress div even if we error
|
2956 |
+
restore_error_handler();
|
2957 |
return false;
|
2958 |
}
|
2959 |
|
2961 |
}
|
2962 |
$updraftplus_restorer->delete = (UpdraftPlus_Options::get_updraft_option('updraft_delete_local')) ? true : false;
|
2963 |
if ('none' === $service || empty($service) || (is_array($service) && 1 == count($service) && (in_array('none', $service) || in_array('', $service)))) {
|
2964 |
+
if ($updraftplus_restorer->delete) $updraftplus->log_e('Will not delete any archives after unpacking them, because there was no cloud storage for this backup');
|
2965 |
$updraftplus_restorer->delete = false;
|
2966 |
}
|
2967 |
|
2972 |
$info = (isset($backupable_entities[$type])) ? $backupable_entities[$type] : array();
|
2973 |
|
2974 |
echo ('db' == $type) ? "<h2>".__('Database','updraftplus')."</h2>" : "<h2>".$info['description']."</h2>";
|
2975 |
+
$updraftplus->log("Entity: ".$type);
|
2976 |
|
2977 |
if (is_string($files)) $files = array($files);
|
2978 |
foreach ($files as $file) {
|
2979 |
$val = $updraftplus_restorer->restore_backup($file, $type, $info);
|
2980 |
|
2981 |
if(is_wp_error($val)) {
|
2982 |
+
$updraftplus->log_e($val);
|
2983 |
foreach ($val->get_error_messages() as $msg) {
|
2984 |
echo '<strong>'.__('Error message', 'updraftplus').':</strong> '.htmlspecialchars($msg).'<br>';
|
2985 |
}
|
2986 |
+
$codes = $val->get_error_codes();
|
2987 |
+
if (is_array($codes)) {
|
2988 |
+
foreach ($codes as $code) {
|
2989 |
+
$data = $val->get_error_data($code);
|
2990 |
+
if (!empty($data)) {
|
2991 |
+
echo '<strong>'.__('Error data:', 'updraftplus').'</strong> '.htmlspecialchars(serialize($data)).'<br>';
|
2992 |
+
}
|
2993 |
+
}
|
2994 |
+
}
|
2995 |
echo '</div>'; //close the updraft_restore_progress div even if we error
|
2996 |
+
restore_error_handler();
|
2997 |
return $val;
|
2998 |
} elseif (false === $val) {
|
2999 |
echo '</div>'; //close the updraft_restore_progress div even if we error
|
3000 |
+
restore_error_handler();
|
3001 |
return false;
|
3002 |
}
|
3003 |
}
|
3004 |
}
|
3005 |
|
3006 |
echo '</div>'; //close the updraft_restore_progress div
|
3007 |
+
|
3008 |
+
restore_error_handler();
|
3009 |
return true;
|
3010 |
}
|
3011 |
|
3012 |
function sort_restoration_entities($a, $b) {
|
3013 |
if ($a == $b) return 0;
|
3014 |
# Put the database first
|
3015 |
+
if ('db' == $a) return -1;
|
3016 |
+
if ('db' == $b) return 1;
|
3017 |
return strcmp($a, $b);
|
3018 |
}
|
3019 |
|
3020 |
+
function return_array($input) {
|
3021 |
+
if (!is_array($input)) $input = array();
|
3022 |
+
return $input;
|
3023 |
+
}
|
3024 |
+
|
3025 |
function get_settings_keys() {
|
3026 |
+
return array('updraft_autobackup_default', 'updraftplus_tmp_googledrive_access_token', 'updraftplus_dismissedautobackup', 'updraft_interval', 'updraft_interval_database', 'updraft_retain', 'updraft_retain_db', 'updraft_encryptionphrase', 'updraft_service', 'updraft_dropbox_appkey', 'updraft_dropbox_secret', 'updraft_googledrive_clientid', 'updraft_googledrive_secret', 'updraft_googledrive_remotepath', 'updraft_ftp_login', 'updraft_ftp_pass', 'updraft_ftp_remote_path', 'updraft_server_address', 'updraft_dir', 'updraft_email', 'updraft_delete_local', 'updraft_debug_mode', 'updraft_include_plugins', 'updraft_include_themes', 'updraft_include_uploads', 'updraft_include_others', 'updraft_include_wpcore', 'updraft_include_wpcore_exclude', 'updraft_include_more', 'updraft_include_blogs', 'updraft_include_mu-plugins', 'updraft_include_others_exclude', 'updraft_lastmessage', 'updraft_googledrive_token', 'updraft_dropboxtk_request_token', 'updraft_dropboxtk_access_token', 'updraft_dropbox_folder',
|
3027 |
+
'updraft_last_backup', 'updraft_starttime_files', 'updraft_starttime_db', 'updraft_startday_db', 'updraft_startday_files', 'updraft_sftp_settings', 'updraft_s3generic_login', 'updraft_s3generic_pass', 'updraft_s3generic_remote_path', 'updraft_s3generic_endpoint', 'updraft_webdav_settings', 'updraft_disable_ping', 'updraft_cloudfiles', 'updraft_cloudfiles_user', 'updraft_cloudfiles_apikey', 'updraft_cloudfiles_path', 'updraft_cloudfiles_authurl', 'updraft_ssl_useservercerts', 'updraft_ssl_disableverify', 'updraft_s3_login', 'updraft_s3_pass', 'updraft_s3_remote_path', 'updraft_dreamobjects_login', 'updraft_dreamobjects_pass', 'updraft_dreamobjects_remote_path', 'updraft_report_warningsonly', 'updraft_report_wholebackup');
|
3028 |
}
|
3029 |
|
3030 |
}
|
@@ -1,14 +1,10 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
if (!defined
|
4 |
-
|
5 |
if (!class_exists('UpdraftPlus_PclZip')) require(UPDRAFTPLUS_DIR.'/class-zip.php');
|
6 |
|
7 |
// This file contains functions that are only needed/loaded when a backup is running (reduces memory usage on other site pages)
|
8 |
|
9 |
-
global $updraftplus_backup;
|
10 |
-
$updraftplus_backup = new UpdraftPlus_Backup();
|
11 |
-
|
12 |
class UpdraftPlus_Backup {
|
13 |
|
14 |
public $index = 0;
|
@@ -34,7 +30,7 @@ class UpdraftPlus_Backup {
|
|
34 |
private $updraft_dir;
|
35 |
private $job_file_entities = array();
|
36 |
|
37 |
-
public function __construct() {
|
38 |
|
39 |
global $updraftplus;
|
40 |
|
@@ -43,6 +39,11 @@ class UpdraftPlus_Backup {
|
|
43 |
$this->debug = UpdraftPlus_Options::get_updraft_option('updraft_debug_mode');
|
44 |
$this->updraft_dir = $updraftplus->backups_dir_location();
|
45 |
|
|
|
|
|
|
|
|
|
|
|
46 |
// false means 'tried + failed'; whereas 0 means 'not yet tried'
|
47 |
// Disallow binzip on OpenVZ when we're not sure there's plenty of memory
|
48 |
if ($this->binzip === 0 && (!defined('UPDRAFTPLUS_PREFERPCLZIP') || UPDRAFTPLUS_PREFERPCLZIP != true) && (!defined('UPDRAFTPLUS_NO_BINZIP') || !UPDRAFTPLUS_NO_BINZIP) && $updraftplus->current_resumption <9) {
|
@@ -152,7 +153,7 @@ class UpdraftPlus_Backup {
|
|
152 |
if ('.' == $e || '..' == $e || !is_file($this->updraft_dir.'/'.$e)) continue;
|
153 |
$ziparchive_match = preg_match("/$match([0-9]+)?\.zip\.tmp\.([A-Za-z0-9]){6}?$/i", $e);
|
154 |
$binzip_match = preg_match("/^zi([A-Za-z0-9]){6}$/", $e);
|
155 |
-
if ($time_now-filemtime($this->updraft_dir.'/'.$e) < 30 && ($ziparchive_match || $binzip_match)) {
|
156 |
$updraftplus->terminate_due_to_activity($this->updraft_dir.'/'.$e, $time_now, filemtime($this->updraft_dir.'/'.$e));
|
157 |
}
|
158 |
}
|
@@ -241,12 +242,14 @@ class UpdraftPlus_Backup {
|
|
241 |
if (1 == ($updraftplus->current_resumption % 2) && count($services)>2) array_push($services, array_shift($services));
|
242 |
}
|
243 |
|
|
|
|
|
244 |
foreach ($services as $ind => $service) {
|
245 |
|
246 |
# Used for logging by record_upload_chunk()
|
247 |
$this->current_service = $service;
|
248 |
# Used when deciding whether to delete the local file
|
249 |
-
$this->last_service = ($ind+1 >= count($services)) ? true : false;
|
250 |
|
251 |
$updraftplus->log("Cloud backup selection: ".$service);
|
252 |
@set_time_limit(900);
|
@@ -440,33 +443,27 @@ class UpdraftPlus_Backup {
|
|
440 |
|
441 |
$debug_mode = UpdraftPlus_Options::get_updraft_option('updraft_debug_mode');
|
442 |
|
443 |
-
$sendmail_to = UpdraftPlus_Options::get_updraft_option('updraft_email');
|
444 |
-
if (
|
445 |
-
|
446 |
-
$admin_email= get_bloginfo('admin_email');
|
447 |
-
foreach (explode(',', $sendmail_to) as $sendmail_addr) {
|
448 |
-
if ($updraftplus->have_addons < 10 && $sendmail_addr != $admin_email) {
|
449 |
-
$updraftplus->log(sprintf(__("With the next release of UpdraftPlus, you will need an add-on to use a different email address to the site owner's (%s). See: %s", 'updraftplus'), $admin_email, 'http://updraftplus.com/next-updraftplus-release-ready-testing/'), 'warning', 'needpremiumforemail');
|
450 |
-
}
|
451 |
-
}
|
452 |
|
453 |
$backup_files = $updraftplus->jobdata_get('backup_files');
|
454 |
$backup_db = $updraftplus->jobdata_get('backup_database');
|
455 |
|
456 |
-
if (
|
457 |
-
$backup_contains = "Files and database";
|
458 |
-
} elseif (
|
459 |
-
$backup_contains = ($backup_db == "begun") ? "Files (database backup has not completed)" : "Files only (database was not part of this particular schedule)";
|
460 |
} elseif ($backup_db == 'finished' || $backup_db == 'encrypted') {
|
461 |
-
$backup_contains = ($backup_files == "begun") ? "Database (files backup has not completed)" : "Database only (files were not part of this particular schedule)";
|
462 |
} else {
|
463 |
-
$backup_contains = "Unknown/unexpected error - please raise a support request";
|
464 |
}
|
465 |
|
466 |
-
$updraftplus->log("Sending email ('$backup_contains') report to: ".substr($sendmail_to, 0, 5)."...");
|
467 |
-
|
468 |
$append_log = '';
|
469 |
$attachments = array();
|
|
|
|
|
|
|
470 |
if ($updraftplus->error_count() > 0) {
|
471 |
$append_log .= __('Errors encountered:', 'updraftplus')."\r\n";
|
472 |
$attachments[0] = $updraftplus->logfile_name;
|
@@ -480,8 +477,9 @@ class UpdraftPlus_Backup {
|
|
480 |
} elseif (is_string($err)) {
|
481 |
$append_log .= "* ".rtrim($err)."\r\n";
|
482 |
}
|
|
|
483 |
}
|
484 |
-
$append_log.="\n";
|
485 |
}
|
486 |
$warnings = $updraftplus->jobdata_get('warnings');
|
487 |
if (is_array($warnings) && count($warnings) >0) {
|
@@ -490,20 +488,66 @@ class UpdraftPlus_Backup {
|
|
490 |
foreach ($warnings as $err) {
|
491 |
$append_log .= "* ".rtrim($err)."\r\n";
|
492 |
}
|
493 |
-
$append_log.="\n";
|
494 |
}
|
495 |
|
496 |
-
|
|
|
|
|
|
|
497 |
|
498 |
// We have to use the action in order to set the MIME type on the attachment - by default, WordPress just puts application/octet-stream
|
499 |
-
if (count($attachments)>0) add_action('phpmailer_init', array($this, 'phpmailer_init'));
|
500 |
|
501 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
502 |
|
503 |
-
|
504 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
505 |
}
|
506 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
507 |
}
|
508 |
|
509 |
// The purpose of this function is to make sure that the options table is put in the database first, then the users table, then the usermeta table; and after that the core WP tables - so that when restoring we restore the core tables first
|
@@ -699,7 +743,6 @@ class UpdraftPlus_Backup {
|
|
699 |
if (!is_array($files)) $files=array($files);
|
700 |
foreach ($files as $file) $updraftplus->check_recent_modification($this->updraft_dir.'/'.$file);
|
701 |
}
|
702 |
-
|
703 |
} elseif ('begun' == $bfiles_status) {
|
704 |
if ($resumption_no>0) {
|
705 |
$updraftplus->log("Creation of backups of directories: had begun; will resume");
|
@@ -794,6 +837,8 @@ class UpdraftPlus_Backup {
|
|
794 |
|
795 |
$how_many_tables = count($all_tables);
|
796 |
|
|
|
|
|
797 |
foreach ($all_tables as $table) {
|
798 |
|
799 |
$manyrows_warning = false;
|
@@ -803,6 +848,9 @@ class UpdraftPlus_Backup {
|
|
803 |
@set_time_limit(900);
|
804 |
// The table file may already exist if we have produced it on a previous run
|
805 |
$table_file_prefix = $file_base.'-db-table-'.$table.'.table';
|
|
|
|
|
|
|
806 |
if (file_exists($this->updraft_dir.'/'.$table_file_prefix.'.gz')) {
|
807 |
$updraftplus->log("Table $table: corresponding file already exists; moving on");
|
808 |
$stitch_files[] = $table_file_prefix;
|
@@ -854,12 +902,28 @@ class UpdraftPlus_Backup {
|
|
854 |
$stitch_files[] = $table_file_prefix;
|
855 |
|
856 |
} else {
|
|
|
857 |
$updraftplus->log("Skipping table (lacks our prefix): $table");
|
858 |
}
|
859 |
|
860 |
}
|
861 |
}
|
862 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
863 |
// Race detection - with zip files now being resumable, these can more easily occur, with two running side-by-side
|
864 |
$backup_final_file_name = $backup_file_base.'-db.gz';
|
865 |
$time_now = time();
|
@@ -878,8 +942,9 @@ class UpdraftPlus_Backup {
|
|
878 |
// We delay the unlinking because if two runs go concurrently and fail to detect each other (should not happen, but there's no harm in assuming the detection failed) then that leads to files missing from the db dump
|
879 |
$unlink_files = array();
|
880 |
|
|
|
881 |
foreach ($stitch_files as $table_file) {
|
882 |
-
$updraftplus->log("{$table_file}.gz: adding to final database dump");
|
883 |
if (!$handle = gzopen($this->updraft_dir.'/'.$table_file.'.gz', "r")) {
|
884 |
$updraftplus->log("Error: Failed to open database file for reading: ${table_file}.gz");
|
885 |
$updraftplus->log("Failed to open database file for reading: ${table_file}.gz", 'error');
|
@@ -889,6 +954,7 @@ class UpdraftPlus_Backup {
|
|
889 |
gzclose($handle);
|
890 |
$unlink_files[] = $this->updraft_dir.'/'.$table_file.'.gz';
|
891 |
}
|
|
|
892 |
}
|
893 |
|
894 |
if (defined("DB_CHARSET")) {
|
@@ -1183,9 +1249,9 @@ class UpdraftPlus_Backup {
|
|
1183 |
$this->stow("# WordPress MySQL database backup\n");
|
1184 |
$this->stow("# Created by UpdraftPlus version ".$updraftplus->version." (http://updraftplus.com)\n");
|
1185 |
$this->stow("# WordPress Version: $wp_version, running on PHP ".phpversion()." (".$_SERVER["SERVER_SOFTWARE"]."), MySQL $mysql_version\n");
|
1186 |
-
$this->stow("# Backup of: ".site_url()."\n");
|
1187 |
-
$this->stow("# Home URL: ".home_url()."\n");
|
1188 |
-
$this->stow("# Content URL: ".content_url()."\n");
|
1189 |
$this->stow("# Table prefix: ".$this->table_prefix_raw."\n");
|
1190 |
$this->stow("# Site info: multisite=".(is_multisite() ? '1' : '0')."\n");
|
1191 |
$this->stow("# Site info: end\n");
|
@@ -1207,7 +1273,11 @@ class UpdraftPlus_Backup {
|
|
1207 |
|
1208 |
public function phpmailer_init($phpmailer) {
|
1209 |
global $updraftplus;
|
1210 |
-
$
|
|
|
|
|
|
|
|
|
1211 |
}
|
1212 |
|
1213 |
// This function recursively packs the zip, dereferencing symlinks but packing into a single-parent tree for universal unpacking
|
@@ -1268,7 +1338,7 @@ class UpdraftPlus_Backup {
|
|
1268 |
#@touch($zipfile);
|
1269 |
} else {
|
1270 |
$updraftplus->log("$fullpath/$e: unreadable file");
|
1271 |
-
$updraftplus->log(sprintf(__("%s: unreadable file - could not be backed up", 'updraftplus'), $use_path_when_storing.'/'.$e), 'warning');
|
1272 |
}
|
1273 |
} elseif (is_dir($fullpath.'/'.$e)) {
|
1274 |
// no need to addEmptyDir here, as it gets done when we recurse
|
1 |
<?php
|
2 |
|
3 |
+
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
|
|
4 |
if (!class_exists('UpdraftPlus_PclZip')) require(UPDRAFTPLUS_DIR.'/class-zip.php');
|
5 |
|
6 |
// This file contains functions that are only needed/loaded when a backup is running (reduces memory usage on other site pages)
|
7 |
|
|
|
|
|
|
|
8 |
class UpdraftPlus_Backup {
|
9 |
|
10 |
public $index = 0;
|
30 |
private $updraft_dir;
|
31 |
private $job_file_entities = array();
|
32 |
|
33 |
+
public function __construct($backup_files) {
|
34 |
|
35 |
global $updraftplus;
|
36 |
|
39 |
$this->debug = UpdraftPlus_Options::get_updraft_option('updraft_debug_mode');
|
40 |
$this->updraft_dir = $updraftplus->backups_dir_location();
|
41 |
|
42 |
+
if ('no' === $backup_files) {
|
43 |
+
$this->use_zip_object = 'UpdraftPlus_PclZip';
|
44 |
+
return;
|
45 |
+
}
|
46 |
+
|
47 |
// false means 'tried + failed'; whereas 0 means 'not yet tried'
|
48 |
// Disallow binzip on OpenVZ when we're not sure there's plenty of memory
|
49 |
if ($this->binzip === 0 && (!defined('UPDRAFTPLUS_PREFERPCLZIP') || UPDRAFTPLUS_PREFERPCLZIP != true) && (!defined('UPDRAFTPLUS_NO_BINZIP') || !UPDRAFTPLUS_NO_BINZIP) && $updraftplus->current_resumption <9) {
|
153 |
if ('.' == $e || '..' == $e || !is_file($this->updraft_dir.'/'.$e)) continue;
|
154 |
$ziparchive_match = preg_match("/$match([0-9]+)?\.zip\.tmp\.([A-Za-z0-9]){6}?$/i", $e);
|
155 |
$binzip_match = preg_match("/^zi([A-Za-z0-9]){6}$/", $e);
|
156 |
+
if ($time_now-filemtime($this->updraft_dir.'/'.$e) < 30 && ($ziparchive_match || (0 != $updraftplus->current_resumption && $binzip_match))) {
|
157 |
$updraftplus->terminate_due_to_activity($this->updraft_dir.'/'.$e, $time_now, filemtime($this->updraft_dir.'/'.$e));
|
158 |
}
|
159 |
}
|
242 |
if (1 == ($updraftplus->current_resumption % 2) && count($services)>2) array_push($services, array_shift($services));
|
243 |
}
|
244 |
|
245 |
+
$errors_before_uploads = $updraftplus->error_count();
|
246 |
+
|
247 |
foreach ($services as $ind => $service) {
|
248 |
|
249 |
# Used for logging by record_upload_chunk()
|
250 |
$this->current_service = $service;
|
251 |
# Used when deciding whether to delete the local file
|
252 |
+
$this->last_service = ($ind+1 >= count($services) && $errors_before_uploads == $updraftplus->error_count()) ? true : false;
|
253 |
|
254 |
$updraftplus->log("Cloud backup selection: ".$service);
|
255 |
@set_time_limit(900);
|
443 |
|
444 |
$debug_mode = UpdraftPlus_Options::get_updraft_option('updraft_debug_mode');
|
445 |
|
446 |
+
$sendmail_to = $updraftplus->just_one_email(UpdraftPlus_Options::get_updraft_option('updraft_email'));
|
447 |
+
if (is_string($sendmail_to)) $sendmail_to = array($sendmail_to);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
448 |
|
449 |
$backup_files = $updraftplus->jobdata_get('backup_files');
|
450 |
$backup_db = $updraftplus->jobdata_get('backup_database');
|
451 |
|
452 |
+
if ('finished' == $backup_files && ('finished' == $backup_db || 'encrypted' == $backup_db)) {
|
453 |
+
$backup_contains = __("Files and database", 'updraftplus');
|
454 |
+
} elseif ('finished' == $backup_files) {
|
455 |
+
$backup_contains = ($backup_db == "begun") ? __("Files (database backup has not completed)", 'updraftplus') : __("Files only (database was not part of this particular schedule)", 'updraftplus');
|
456 |
} elseif ($backup_db == 'finished' || $backup_db == 'encrypted') {
|
457 |
+
$backup_contains = ($backup_files == "begun") ? __("Database (files backup has not completed)", 'updraftplus') : __("Database only (files were not part of this particular schedule)", 'updraftplus');
|
458 |
} else {
|
459 |
+
$backup_contains = __("Unknown/unexpected error - please raise a support request", 'updraftplus');
|
460 |
}
|
461 |
|
|
|
|
|
462 |
$append_log = '';
|
463 |
$attachments = array();
|
464 |
+
|
465 |
+
$error_count = 0;
|
466 |
+
|
467 |
if ($updraftplus->error_count() > 0) {
|
468 |
$append_log .= __('Errors encountered:', 'updraftplus')."\r\n";
|
469 |
$attachments[0] = $updraftplus->logfile_name;
|
477 |
} elseif (is_string($err)) {
|
478 |
$append_log .= "* ".rtrim($err)."\r\n";
|
479 |
}
|
480 |
+
$error_count++;
|
481 |
}
|
482 |
+
$append_log.="\r\n";
|
483 |
}
|
484 |
$warnings = $updraftplus->jobdata_get('warnings');
|
485 |
if (is_array($warnings) && count($warnings) >0) {
|
488 |
foreach ($warnings as $err) {
|
489 |
$append_log .= "* ".rtrim($err)."\r\n";
|
490 |
}
|
491 |
+
$append_log.="\r\n";
|
492 |
}
|
493 |
|
494 |
+
if ($debug_mode && '' != $updraftplus->logfile_name && !in_array($updraftplus->logfile_name, $attachments)) {
|
495 |
+
$append_log .= "\r\n".__('The log file has been attached to this email.', 'updraftplus');
|
496 |
+
$attachments[0] = $updraftplus->logfile_name;
|
497 |
+
}
|
498 |
|
499 |
// We have to use the action in order to set the MIME type on the attachment - by default, WordPress just puts application/octet-stream
|
|
|
500 |
|
501 |
+
$subject = apply_filters('updraft_report_subject', sprintf(__('Backed up: %s', 'updraftplus'), get_bloginfo('name')).' (UpdraftPlus '.$updraftplus->version.') '.get_date_from_gmt(gmdate('Y-m-d H:i:s', time()), 'Y-m-d H:i'), $error_count, count($warnings));
|
502 |
+
|
503 |
+
$body = apply_filters('updraft_report_body', __('Backup of:').' '.site_url()."\r\nUpdraftPlus ".__('WordPress backup is complete','updraftplus').".\r\n".__('Backup contains:','updraftplus').' '.$backup_contains."\r\n".__('Latest status:', 'updraftplus').' '.$final_message."\r\n\r\n".$updraftplus->wordshell_random_advert(0)."\r\n".$append_log, $final_message, $backup_contains, $updraftplus->errors, $warnings);
|
504 |
+
|
505 |
+
$this->attachments = apply_filters('updraft_report_attachments', $attachments);
|
506 |
+
|
507 |
+
if (count($this->attachments)>0) add_action('phpmailer_init', array($this, 'phpmailer_init'));
|
508 |
|
509 |
+
$attach_size = 0;
|
510 |
+
$unlink_files = array();
|
511 |
+
|
512 |
+
foreach ($this->attachments as $ind => $attach) {
|
513 |
+
if ($attach == $updraftplus->logfile_name && filesize($attach) > 6*1048576) {
|
514 |
+
|
515 |
+
$updraftplus->log("Log file is large (".round(filesize($attach)/1024, 1)." Kb): will compress before e-mailing");
|
516 |
+
|
517 |
+
if (!$handle = fopen($attach, "r")) {
|
518 |
+
$updraftplus->log("Error: Failed to open log file for reading: ".$attach);
|
519 |
+
} else {
|
520 |
+
if (!$whandle = gzopen($attach.'.gz', 'w')) {
|
521 |
+
$updraftplus->log("Error: Failed to open log file for reading: ".$attach.".gz");
|
522 |
+
} else {
|
523 |
+
while (false !== ($line = @stream_get_line($handle, 2048, "\n"))) {
|
524 |
+
@gzwrite($whandle, $line."\n");
|
525 |
+
}
|
526 |
+
fclose($handle);
|
527 |
+
gzclose($whandle);
|
528 |
+
$this->attachments[$ind] = $attach.'.gz';
|
529 |
+
$unlink_files[] = $attach.'.gz';
|
530 |
+
}
|
531 |
+
}
|
532 |
+
}
|
533 |
+
$attach_size += filesize($this->attachments[$ind]);
|
534 |
}
|
535 |
|
536 |
+
foreach ($sendmail_to as $ind => $mailto) {
|
537 |
+
|
538 |
+
if (false === apply_filters('updraft_report_sendto', true, $mailto, $error_count, count($warnings), $ind)) continue;
|
539 |
+
|
540 |
+
foreach (explode(',', $mailto) as $sendmail_addr) {
|
541 |
+
$updraftplus->log("Sending email ('$backup_contains') report (attachments: ".count($attachments).", size: ".round($attach_size/1024, 1)." Kb) to: ".substr($sendmail_addr, 0, 5)."...");
|
542 |
+
wp_mail(trim($sendmail_addr), $subject, $body);
|
543 |
+
}
|
544 |
+
}
|
545 |
+
|
546 |
+
foreach ($unlink_files as $file) @unlink($file);
|
547 |
+
|
548 |
+
do_action('updraft_report_finished');
|
549 |
+
if (count($this->attachments)>0) remove_action('phpmailer_init', array($this, 'phpmailer_init'));
|
550 |
+
|
551 |
}
|
552 |
|
553 |
// The purpose of this function is to make sure that the options table is put in the database first, then the users table, then the usermeta table; and after that the core WP tables - so that when restoring we restore the core tables first
|
743 |
if (!is_array($files)) $files=array($files);
|
744 |
foreach ($files as $file) $updraftplus->check_recent_modification($this->updraft_dir.'/'.$file);
|
745 |
}
|
|
|
746 |
} elseif ('begun' == $bfiles_status) {
|
747 |
if ($resumption_no>0) {
|
748 |
$updraftplus->log("Creation of backups of directories: had begun; will resume");
|
837 |
|
838 |
$how_many_tables = count($all_tables);
|
839 |
|
840 |
+
$found_options_table = false;
|
841 |
+
|
842 |
foreach ($all_tables as $table) {
|
843 |
|
844 |
$manyrows_warning = false;
|
848 |
@set_time_limit(900);
|
849 |
// The table file may already exist if we have produced it on a previous run
|
850 |
$table_file_prefix = $file_base.'-db-table-'.$table.'.table';
|
851 |
+
|
852 |
+
if ($this->table_prefix.'options' == $table) $found_options_table = true;
|
853 |
+
|
854 |
if (file_exists($this->updraft_dir.'/'.$table_file_prefix.'.gz')) {
|
855 |
$updraftplus->log("Table $table: corresponding file already exists; moving on");
|
856 |
$stitch_files[] = $table_file_prefix;
|
902 |
$stitch_files[] = $table_file_prefix;
|
903 |
|
904 |
} else {
|
905 |
+
$total_tables--;
|
906 |
$updraftplus->log("Skipping table (lacks our prefix): $table");
|
907 |
}
|
908 |
|
909 |
}
|
910 |
}
|
911 |
|
912 |
+
if (!$found_options_table) {
|
913 |
+
$updraftplus->log(__('The database backup appears to have failed - the options table was not found', 'updraftplus'), 'warning', 'optstablenotfound');
|
914 |
+
$time_this_run = time()-$updraftplus->opened_log_time;
|
915 |
+
if ($time_this_run > 2000) {
|
916 |
+
# Have seen this happen; not sure how, but it was apparently deterministic; if the current process had been running for a long time, then apparently all database commands silently failed.
|
917 |
+
# If we have been running that long, then the resumption may be far off; bring it closer
|
918 |
+
$updraftplus->reschedule(60);
|
919 |
+
$updraftplus->log("Have been running very long, and it seems the database went away; terminating");
|
920 |
+
$updraftplus->record_still_alive();
|
921 |
+
die;
|
922 |
+
}
|
923 |
+
} else {
|
924 |
+
$updraftplus->log_removewarning('optstablenotfound');
|
925 |
+
}
|
926 |
+
|
927 |
// Race detection - with zip files now being resumable, these can more easily occur, with two running side-by-side
|
928 |
$backup_final_file_name = $backup_file_base.'-db.gz';
|
929 |
$time_now = time();
|
942 |
// We delay the unlinking because if two runs go concurrently and fail to detect each other (should not happen, but there's no harm in assuming the detection failed) then that leads to files missing from the db dump
|
943 |
$unlink_files = array();
|
944 |
|
945 |
+
$sind = 1;
|
946 |
foreach ($stitch_files as $table_file) {
|
947 |
+
$updraftplus->log("{$table_file}.gz ($sind/$how_many_tables): adding to final database dump");
|
948 |
if (!$handle = gzopen($this->updraft_dir.'/'.$table_file.'.gz', "r")) {
|
949 |
$updraftplus->log("Error: Failed to open database file for reading: ${table_file}.gz");
|
950 |
$updraftplus->log("Failed to open database file for reading: ${table_file}.gz", 'error');
|
954 |
gzclose($handle);
|
955 |
$unlink_files[] = $this->updraft_dir.'/'.$table_file.'.gz';
|
956 |
}
|
957 |
+
$sind++;
|
958 |
}
|
959 |
|
960 |
if (defined("DB_CHARSET")) {
|
1249 |
$this->stow("# WordPress MySQL database backup\n");
|
1250 |
$this->stow("# Created by UpdraftPlus version ".$updraftplus->version." (http://updraftplus.com)\n");
|
1251 |
$this->stow("# WordPress Version: $wp_version, running on PHP ".phpversion()." (".$_SERVER["SERVER_SOFTWARE"]."), MySQL $mysql_version\n");
|
1252 |
+
$this->stow("# Backup of: ".untrailingslashit(site_url())."\n");
|
1253 |
+
$this->stow("# Home URL: ".untrailingslashit(home_url())."\n");
|
1254 |
+
$this->stow("# Content URL: ".untrailingslashit(content_url())."\n");
|
1255 |
$this->stow("# Table prefix: ".$this->table_prefix_raw."\n");
|
1256 |
$this->stow("# Site info: multisite=".(is_multisite() ? '1' : '0')."\n");
|
1257 |
$this->stow("# Site info: end\n");
|
1273 |
|
1274 |
public function phpmailer_init($phpmailer) {
|
1275 |
global $updraftplus;
|
1276 |
+
if (empty($this->attachments) || !is_array($this->attachments)) return;
|
1277 |
+
foreach ($this->attachments as $attach) {
|
1278 |
+
$mime_type = (preg_match('/\.gz$/', $attach)) ? 'application/x-gzip' : 'text/plain';
|
1279 |
+
$phpmailer->AddAttachment($attach, '', 'base64', $mime_type);
|
1280 |
+
}
|
1281 |
}
|
1282 |
|
1283 |
// This function recursively packs the zip, dereferencing symlinks but packing into a single-parent tree for universal unpacking
|
1338 |
#@touch($zipfile);
|
1339 |
} else {
|
1340 |
$updraftplus->log("$fullpath/$e: unreadable file");
|
1341 |
+
$updraftplus->log(sprintf(__("%s: unreadable file - could not be backed up", 'updraftplus'), $use_path_when_storing.'/'.$e), 'warning', "unrfile-$e");
|
1342 |
}
|
1343 |
} elseif (is_dir($fullpath.'/'.$e)) {
|
1344 |
// no need to addEmptyDir here, as it gets done when we recurse
|
Binary file
|
@@ -49,7 +49,7 @@ abstract class Dropbox_ConsumerAbstract
|
|
49 |
$this->getAccessToken();
|
50 |
} catch(Dropbox_Exception $e) {
|
51 |
global $updraftplus;
|
52 |
-
$updraftplus->log($e->getMessage().' - need to reauthenticate this site with Dropbox (if this fails, then you can also try wiping your settings from the Expert Settings section');
|
53 |
$this->getRequestToken();
|
54 |
$this->authorise();
|
55 |
}
|
@@ -80,22 +80,23 @@ abstract class Dropbox_ConsumerAbstract
|
|
80 |
private function authorise()
|
81 |
{
|
82 |
// Only redirect if not using CLI
|
83 |
-
if (PHP_SAPI !== 'cli' && (!defined('DOING_CRON') || !DOING_CRON)) {
|
84 |
$url = $this->getAuthoriseUrl();
|
85 |
if (!headers_sent()) {
|
86 |
header('Location: ' . $url);
|
|
|
87 |
} else {
|
88 |
throw new Dropbox_Exception(sprintf(__('The %s authentication could not go ahead, because something else on your site is breaking it. Try disabling your other plugins and switching to a default theme. (Specifically, you are looking for the component that sends output (most likely PHP warnings/errors) before the page begins. Turning off any debugging settings may also help).', ''), 'Dropbox'));
|
89 |
}
|
90 |
-
exit;
|
91 |
?><?php
|
92 |
-
return;
|
93 |
}
|
94 |
global $updraftplus;
|
95 |
-
$updraftplus->log('Dropbox reauthorisation needed; but we are running from cron or the CLI, so this is not possible');
|
96 |
-
UpdraftPlus_Options::update_updraft_option("updraft_dropboxtk_request_token",'');
|
97 |
-
|
98 |
-
|
|
|
99 |
}
|
100 |
|
101 |
/**
|
@@ -193,7 +194,8 @@ abstract class Dropbox_ConsumerAbstract
|
|
193 |
// If the value is a file upload (prefixed with @), replace it with
|
194 |
// the destination filename, the file path will be sent in POSTFIELDS
|
195 |
if (isset($value[0]) && $value[0] === '@') $value = $params['filename'];
|
196 |
-
|
|
|
197 |
} else {
|
198 |
unset($params[$param]);
|
199 |
}
|
49 |
$this->getAccessToken();
|
50 |
} catch(Dropbox_Exception $e) {
|
51 |
global $updraftplus;
|
52 |
+
$updraftplus->log($e->getMessage().' - need to reauthenticate this site with Dropbox (if this fails, then you can also try wiping your settings from the Expert Settings section)');
|
53 |
$this->getRequestToken();
|
54 |
$this->authorise();
|
55 |
}
|
80 |
private function authorise()
|
81 |
{
|
82 |
// Only redirect if not using CLI
|
83 |
+
if (PHP_SAPI !== 'cli' && (!defined('DOING_CRON') || !DOING_CRON) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
|
84 |
$url = $this->getAuthoriseUrl();
|
85 |
if (!headers_sent()) {
|
86 |
header('Location: ' . $url);
|
87 |
+
exit;
|
88 |
} else {
|
89 |
throw new Dropbox_Exception(sprintf(__('The %s authentication could not go ahead, because something else on your site is breaking it. Try disabling your other plugins and switching to a default theme. (Specifically, you are looking for the component that sends output (most likely PHP warnings/errors) before the page begins. Turning off any debugging settings may also help).', ''), 'Dropbox'));
|
90 |
}
|
|
|
91 |
?><?php
|
92 |
+
return false;
|
93 |
}
|
94 |
global $updraftplus;
|
95 |
+
$updraftplus->log('Dropbox reauthorisation needed; but we are running from cron, AJAX or the CLI, so this is not possible');
|
96 |
+
UpdraftPlus_Options::update_updraft_option("updraft_dropboxtk_request_token", '');
|
97 |
+
throw new Dropbox_Exception(sprintf(__('You need to re-authenticate with %s, as your existing credentials are not working.', 'updraftplus'), 'Dropbox'));
|
98 |
+
#$updraftplus->log(sprintf(__('You need to re-authenticate with %s, as your existing credentials are not working.', 'updraftplus'), 'Dropbox'), 'error');
|
99 |
+
return false;
|
100 |
}
|
101 |
|
102 |
/**
|
194 |
// If the value is a file upload (prefixed with @), replace it with
|
195 |
// the destination filename, the file path will be sent in POSTFIELDS
|
196 |
if (isset($value[0]) && $value[0] === '@') $value = $params['filename'];
|
197 |
+
# Prevent spurious PHP warning by only doing non-arrays
|
198 |
+
if (!is_array($value)) $encoded[] = $this->encode($param) . '=' . $this->encode($value);
|
199 |
} else {
|
200 |
unset($params[$param]);
|
201 |
}
|
@@ -84,6 +84,7 @@ class Dropbox_Curl extends Dropbox_ConsumerAbstract
|
|
84 |
$options[CURLOPT_HEADER] = false;
|
85 |
$options[CURLOPT_FILE] = $this->outFile;
|
86 |
$options[CURLOPT_BINARYTRANSFER] = true;
|
|
|
87 |
if (isset($additional['headers'])) $options[CURLOPT_HTTPHEADER] = $additional['headers'];
|
88 |
$this->outFile = null;
|
89 |
} elseif ($method == 'POST') { // POST
|
@@ -97,13 +98,15 @@ class Dropbox_Curl extends Dropbox_ConsumerAbstract
|
|
97 |
fseek($this->inFile, 0);
|
98 |
$this->inFile = null;
|
99 |
}
|
100 |
-
|
101 |
// Set the cURL options at once
|
102 |
curl_setopt_array($handle, $options);
|
103 |
|
104 |
// Execute, get any error and close
|
105 |
$response = curl_exec($handle);
|
106 |
$error = curl_error($handle);
|
|
|
|
|
107 |
curl_close($handle);
|
108 |
|
109 |
//Check if a cURL error has occured
|
@@ -117,10 +120,12 @@ class Dropbox_Curl extends Dropbox_ConsumerAbstract
|
|
117 |
|
118 |
// Set the last response
|
119 |
$this->lastResponse = $response;
|
|
|
|
|
120 |
|
121 |
// The API doesn't return an error message for the 304 status code...
|
122 |
// 304's are only returned when the path supplied during metadata calls has not been modified
|
123 |
-
if ($
|
124 |
$response['body'] = new stdClass;
|
125 |
$response['body']->error = 'The folder contents have not changed';
|
126 |
}
|
@@ -128,7 +133,7 @@ class Dropbox_Curl extends Dropbox_ConsumerAbstract
|
|
128 |
// Check if an error occurred and throw an Exception
|
129 |
if (!empty($response['body']->error)) {
|
130 |
// Dropbox returns error messages inconsistently...
|
131 |
-
if ($response['body']->error instanceof
|
132 |
$array = array_values((array) $response['body']->error);
|
133 |
$message = $array[0];
|
134 |
} else {
|
@@ -136,7 +141,7 @@ class Dropbox_Curl extends Dropbox_ConsumerAbstract
|
|
136 |
}
|
137 |
|
138 |
// Throw an Exception with the appropriate with the appropriate message and code
|
139 |
-
switch ($
|
140 |
case 304:
|
141 |
throw new Dropbox_NotModifiedException($message, 304);
|
142 |
case 400:
|
@@ -148,7 +153,7 @@ class Dropbox_Curl extends Dropbox_ConsumerAbstract
|
|
148 |
case 415:
|
149 |
throw new Dropbox_UnsupportedMediaTypeException($message, 415);
|
150 |
default:
|
151 |
-
throw new Dropbox_Exception($message, $
|
152 |
}
|
153 |
}
|
154 |
|
84 |
$options[CURLOPT_HEADER] = false;
|
85 |
$options[CURLOPT_FILE] = $this->outFile;
|
86 |
$options[CURLOPT_BINARYTRANSFER] = true;
|
87 |
+
$options[CURLOPT_FAILONERROR] = true;
|
88 |
if (isset($additional['headers'])) $options[CURLOPT_HTTPHEADER] = $additional['headers'];
|
89 |
$this->outFile = null;
|
90 |
} elseif ($method == 'POST') { // POST
|
98 |
fseek($this->inFile, 0);
|
99 |
$this->inFile = null;
|
100 |
}
|
101 |
+
|
102 |
// Set the cURL options at once
|
103 |
curl_setopt_array($handle, $options);
|
104 |
|
105 |
// Execute, get any error and close
|
106 |
$response = curl_exec($handle);
|
107 |
$error = curl_error($handle);
|
108 |
+
$getinfo = curl_getinfo($handle);
|
109 |
+
|
110 |
curl_close($handle);
|
111 |
|
112 |
//Check if a cURL error has occured
|
120 |
|
121 |
// Set the last response
|
122 |
$this->lastResponse = $response;
|
123 |
+
|
124 |
+
$code = (!empty($response['code'])) ? $response['code'] : $getinfo['http_code'];
|
125 |
|
126 |
// The API doesn't return an error message for the 304 status code...
|
127 |
// 304's are only returned when the path supplied during metadata calls has not been modified
|
128 |
+
if ($code == 304) {
|
129 |
$response['body'] = new stdClass;
|
130 |
$response['body']->error = 'The folder contents have not changed';
|
131 |
}
|
133 |
// Check if an error occurred and throw an Exception
|
134 |
if (!empty($response['body']->error)) {
|
135 |
// Dropbox returns error messages inconsistently...
|
136 |
+
if ($response['body']->error instanceof stdClass) {
|
137 |
$array = array_values((array) $response['body']->error);
|
138 |
$message = $array[0];
|
139 |
} else {
|
141 |
}
|
142 |
|
143 |
// Throw an Exception with the appropriate with the appropriate message and code
|
144 |
+
switch ($code) {
|
145 |
case 304:
|
146 |
throw new Dropbox_NotModifiedException($message, 304);
|
147 |
case 400:
|
153 |
case 415:
|
154 |
throw new Dropbox_UnsupportedMediaTypeException($message, 415);
|
155 |
default:
|
156 |
+
throw new Dropbox_Exception($message, $code);
|
157 |
}
|
158 |
}
|
159 |
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload.php @generated by Composer
|
4 |
+
|
5 |
+
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
6 |
+
|
7 |
+
return ComposerAutoloaderInit3357fec9f50023e8458d16284bb22ab5::getLoader();
|
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Composer.
|
5 |
+
*
|
6 |
+
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
+
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Composer\Autoload;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* ClassLoader implements a PSR-0 class loader
|
17 |
+
*
|
18 |
+
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
|
19 |
+
*
|
20 |
+
* $loader = new \Composer\Autoload\ClassLoader();
|
21 |
+
*
|
22 |
+
* // register classes with namespaces
|
23 |
+
* $loader->add('Symfony\Component', __DIR__.'/component');
|
24 |
+
* $loader->add('Symfony', __DIR__.'/framework');
|
25 |
+
*
|
26 |
+
* // activate the autoloader
|
27 |
+
* $loader->register();
|
28 |
+
*
|
29 |
+
* // to enable searching the include path (eg. for PEAR packages)
|
30 |
+
* $loader->setUseIncludePath(true);
|
31 |
+
*
|
32 |
+
* In this example, if you try to use a class in the Symfony\Component
|
33 |
+
* namespace or one of its children (Symfony\Component\Console for instance),
|
34 |
+
* the autoloader will first look for the class under the component/
|
35 |
+
* directory, and it will then fallback to the framework/ directory if not
|
36 |
+
* found before giving up.
|
37 |
+
*
|
38 |
+
* This class is loosely based on the Symfony UniversalClassLoader.
|
39 |
+
*
|
40 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
41 |
+
* @author Jordi Boggiano <j.boggiano@seld.be>
|
42 |
+
*/
|
43 |
+
class ClassLoader
|
44 |
+
{
|
45 |
+
private $prefixes = array();
|
46 |
+
private $fallbackDirs = array();
|
47 |
+
private $useIncludePath = false;
|
48 |
+
private $classMap = array();
|
49 |
+
|
50 |
+
public function getPrefixes()
|
51 |
+
{
|
52 |
+
return call_user_func_array('array_merge', $this->prefixes);
|
53 |
+
}
|
54 |
+
|
55 |
+
public function getFallbackDirs()
|
56 |
+
{
|
57 |
+
return $this->fallbackDirs;
|
58 |
+
}
|
59 |
+
|
60 |
+
public function getClassMap()
|
61 |
+
{
|
62 |
+
return $this->classMap;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* @param array $classMap Class to filename map
|
67 |
+
*/
|
68 |
+
public function addClassMap(array $classMap)
|
69 |
+
{
|
70 |
+
if ($this->classMap) {
|
71 |
+
$this->classMap = array_merge($this->classMap, $classMap);
|
72 |
+
} else {
|
73 |
+
$this->classMap = $classMap;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Registers a set of classes, merging with any others previously set.
|
79 |
+
*
|
80 |
+
* @param string $prefix The classes prefix
|
81 |
+
* @param array|string $paths The location(s) of the classes
|
82 |
+
* @param bool $prepend Prepend the location(s)
|
83 |
+
*/
|
84 |
+
public function add($prefix, $paths, $prepend = false)
|
85 |
+
{
|
86 |
+
if (!$prefix) {
|
87 |
+
if ($prepend) {
|
88 |
+
$this->fallbackDirs = array_merge(
|
89 |
+
(array) $paths,
|
90 |
+
$this->fallbackDirs
|
91 |
+
);
|
92 |
+
} else {
|
93 |
+
$this->fallbackDirs = array_merge(
|
94 |
+
$this->fallbackDirs,
|
95 |
+
(array) $paths
|
96 |
+
);
|
97 |
+
}
|
98 |
+
|
99 |
+
return;
|
100 |
+
}
|
101 |
+
|
102 |
+
$first = $prefix[0];
|
103 |
+
if (!isset($this->prefixes[$first][$prefix])) {
|
104 |
+
$this->prefixes[$first][$prefix] = (array) $paths;
|
105 |
+
|
106 |
+
return;
|
107 |
+
}
|
108 |
+
if ($prepend) {
|
109 |
+
$this->prefixes[$first][$prefix] = array_merge(
|
110 |
+
(array) $paths,
|
111 |
+
$this->prefixes[$first][$prefix]
|
112 |
+
);
|
113 |
+
} else {
|
114 |
+
$this->prefixes[$first][$prefix] = array_merge(
|
115 |
+
$this->prefixes[$first][$prefix],
|
116 |
+
(array) $paths
|
117 |
+
);
|
118 |
+
}
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Registers a set of classes, replacing any others previously set.
|
123 |
+
*
|
124 |
+
* @param string $prefix The classes prefix
|
125 |
+
* @param array|string $paths The location(s) of the classes
|
126 |
+
*/
|
127 |
+
public function set($prefix, $paths)
|
128 |
+
{
|
129 |
+
if (!$prefix) {
|
130 |
+
$this->fallbackDirs = (array) $paths;
|
131 |
+
|
132 |
+
return;
|
133 |
+
}
|
134 |
+
$this->prefixes[substr($prefix, 0, 1)][$prefix] = (array) $paths;
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Turns on searching the include path for class files.
|
139 |
+
*
|
140 |
+
* @param bool $useIncludePath
|
141 |
+
*/
|
142 |
+
public function setUseIncludePath($useIncludePath)
|
143 |
+
{
|
144 |
+
$this->useIncludePath = $useIncludePath;
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Can be used to check if the autoloader uses the include path to check
|
149 |
+
* for classes.
|
150 |
+
*
|
151 |
+
* @return bool
|
152 |
+
*/
|
153 |
+
public function getUseIncludePath()
|
154 |
+
{
|
155 |
+
return $this->useIncludePath;
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Registers this instance as an autoloader.
|
160 |
+
*
|
161 |
+
* @param bool $prepend Whether to prepend the autoloader or not
|
162 |
+
*/
|
163 |
+
public function register($prepend = false)
|
164 |
+
{
|
165 |
+
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Unregisters this instance as an autoloader.
|
170 |
+
*/
|
171 |
+
public function unregister()
|
172 |
+
{
|
173 |
+
spl_autoload_unregister(array($this, 'loadClass'));
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Loads the given class or interface.
|
178 |
+
*
|
179 |
+
* @param string $class The name of the class
|
180 |
+
* @return bool|null True if loaded, null otherwise
|
181 |
+
*/
|
182 |
+
public function loadClass($class)
|
183 |
+
{
|
184 |
+
if ($file = $this->findFile($class)) {
|
185 |
+
include $file;
|
186 |
+
|
187 |
+
return true;
|
188 |
+
}
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Finds the path to the file where the class is defined.
|
193 |
+
*
|
194 |
+
* @param string $class The name of the class
|
195 |
+
*
|
196 |
+
* @return string|false The path if found, false otherwise
|
197 |
+
*/
|
198 |
+
public function findFile($class)
|
199 |
+
{
|
200 |
+
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
201 |
+
if ('\\' == $class[0]) {
|
202 |
+
$class = substr($class, 1);
|
203 |
+
}
|
204 |
+
|
205 |
+
if (isset($this->classMap[$class])) {
|
206 |
+
return $this->classMap[$class];
|
207 |
+
}
|
208 |
+
|
209 |
+
if (false !== $pos = strrpos($class, '\\')) {
|
210 |
+
// namespaced class name
|
211 |
+
$classPath = strtr(substr($class, 0, $pos), '\\', DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
212 |
+
$className = substr($class, $pos + 1);
|
213 |
+
} else {
|
214 |
+
// PEAR-like class name
|
215 |
+
$classPath = null;
|
216 |
+
$className = $class;
|
217 |
+
}
|
218 |
+
|
219 |
+
$classPath .= strtr($className, '_', DIRECTORY_SEPARATOR) . '.php';
|
220 |
+
|
221 |
+
$first = $class[0];
|
222 |
+
if (isset($this->prefixes[$first])) {
|
223 |
+
foreach ($this->prefixes[$first] as $prefix => $dirs) {
|
224 |
+
if (0 === strpos($class, $prefix)) {
|
225 |
+
foreach ($dirs as $dir) {
|
226 |
+
if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) {
|
227 |
+
return $dir . DIRECTORY_SEPARATOR . $classPath;
|
228 |
+
}
|
229 |
+
}
|
230 |
+
}
|
231 |
+
}
|
232 |
+
}
|
233 |
+
|
234 |
+
foreach ($this->fallbackDirs as $dir) {
|
235 |
+
if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) {
|
236 |
+
return $dir . DIRECTORY_SEPARATOR . $classPath;
|
237 |
+
}
|
238 |
+
}
|
239 |
+
|
240 |
+
if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) {
|
241 |
+
return $file;
|
242 |
+
}
|
243 |
+
|
244 |
+
return $this->classMap[$class] = false;
|
245 |
+
}
|
246 |
+
}
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_classmap.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
);
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_namespaces.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
|
10 |
+
'OpenCloud' => array($vendorDir . '/rackspace/php-opencloud/lib', $vendorDir . '/rackspace/php-opencloud/tests'),
|
11 |
+
'Guzzle\\Stream' => array($vendorDir . '/guzzle/stream'),
|
12 |
+
'Guzzle\\Parser' => array($vendorDir . '/guzzle/parser'),
|
13 |
+
'Guzzle\\Http' => array($vendorDir . '/guzzle/http'),
|
14 |
+
'Guzzle\\Common' => array($vendorDir . '/guzzle/common'),
|
15 |
+
);
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_real.php @generated by Composer
|
4 |
+
|
5 |
+
class ComposerAutoloaderInit3357fec9f50023e8458d16284bb22ab5
|
6 |
+
{
|
7 |
+
private static $loader;
|
8 |
+
|
9 |
+
public static function loadClassLoader($class)
|
10 |
+
{
|
11 |
+
if ('Composer\Autoload\ClassLoader' === $class) {
|
12 |
+
require __DIR__ . '/ClassLoader.php';
|
13 |
+
}
|
14 |
+
}
|
15 |
+
|
16 |
+
public static function getLoader()
|
17 |
+
{
|
18 |
+
if (null !== self::$loader) {
|
19 |
+
return self::$loader;
|
20 |
+
}
|
21 |
+
|
22 |
+
spl_autoload_register(array('ComposerAutoloaderInit3357fec9f50023e8458d16284bb22ab5', 'loadClassLoader'), true, true);
|
23 |
+
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit3357fec9f50023e8458d16284bb22ab5', 'loadClassLoader'));
|
25 |
+
|
26 |
+
$vendorDir = dirname(__DIR__);
|
27 |
+
$baseDir = dirname($vendorDir);
|
28 |
+
|
29 |
+
$map = require __DIR__ . '/autoload_namespaces.php';
|
30 |
+
foreach ($map as $namespace => $path) {
|
31 |
+
$loader->set($namespace, $path);
|
32 |
+
}
|
33 |
+
|
34 |
+
$classMap = require __DIR__ . '/autoload_classmap.php';
|
35 |
+
if ($classMap) {
|
36 |
+
$loader->addClassMap($classMap);
|
37 |
+
}
|
38 |
+
|
39 |
+
$loader->register(true);
|
40 |
+
|
41 |
+
return $loader;
|
42 |
+
}
|
43 |
+
}
|
@@ -0,0 +1,321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"name": "symfony/event-dispatcher",
|
4 |
+
"version": "v2.3.7",
|
5 |
+
"version_normalized": "2.3.7.0",
|
6 |
+
"target-dir": "Symfony/Component/EventDispatcher",
|
7 |
+
"source": {
|
8 |
+
"type": "git",
|
9 |
+
"url": "https://github.com/symfony/EventDispatcher.git",
|
10 |
+
"reference": "2d8ece3c610726a73d0c95c885134efea182610e"
|
11 |
+
},
|
12 |
+
"dist": {
|
13 |
+
"type": "zip",
|
14 |
+
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/2d8ece3c610726a73d0c95c885134efea182610e",
|
15 |
+
"reference": "2d8ece3c610726a73d0c95c885134efea182610e",
|
16 |
+
"shasum": ""
|
17 |
+
},
|
18 |
+
"require": {
|
19 |
+
"php": ">=5.3.3"
|
20 |
+
},
|
21 |
+
"require-dev": {
|
22 |
+
"symfony/dependency-injection": "~2.0"
|
23 |
+
},
|
24 |
+
"suggest": {
|
25 |
+
"symfony/dependency-injection": "",
|
26 |
+
"symfony/http-kernel": ""
|
27 |
+
},
|
28 |
+
"time": "2013-10-13 06:32:10",
|
29 |
+
"type": "library",
|
30 |
+
"extra": {
|
31 |
+
"branch-alias": {
|
32 |
+
"dev-master": "2.3-dev"
|
33 |
+
}
|
34 |
+
},
|
35 |
+
"installation-source": "dist",
|
36 |
+
"autoload": {
|
37 |
+
"psr-0": {
|
38 |
+
"Symfony\\Component\\EventDispatcher\\": ""
|
39 |
+
}
|
40 |
+
},
|
41 |
+
"notification-url": "https://packagist.org/downloads/",
|
42 |
+
"license": [
|
43 |
+
"MIT"
|
44 |
+
],
|
45 |
+
"authors": [
|
46 |
+
{
|
47 |
+
"name": "Fabien Potencier",
|
48 |
+
"email": "fabien@symfony.com"
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"name": "Symfony Community",
|
52 |
+
"homepage": "http://symfony.com/contributors"
|
53 |
+
}
|
54 |
+
],
|
55 |
+
"description": "Symfony EventDispatcher Component",
|
56 |
+
"homepage": "http://symfony.com"
|
57 |
+
},
|
58 |
+
{
|
59 |
+
"name": "guzzle/common",
|
60 |
+
"version": "v3.7.4",
|
61 |
+
"version_normalized": "3.7.4.0",
|
62 |
+
"target-dir": "Guzzle/Common",
|
63 |
+
"source": {
|
64 |
+
"type": "git",
|
65 |
+
"url": "https://github.com/guzzle/common.git",
|
66 |
+
"reference": "5126e268446c7e7df961b89128d71878e0652432"
|
67 |
+
},
|
68 |
+
"dist": {
|
69 |
+
"type": "zip",
|
70 |
+
"url": "https://api.github.com/repos/guzzle/common/zipball/5126e268446c7e7df961b89128d71878e0652432",
|
71 |
+
"reference": "5126e268446c7e7df961b89128d71878e0652432",
|
72 |
+
"shasum": ""
|
73 |
+
},
|
74 |
+
"require": {
|
75 |
+
"php": ">=5.3.2",
|
76 |
+
"symfony/event-dispatcher": ">=2.1"
|
77 |
+
},
|
78 |
+
"time": "2013-10-02 20:47:00",
|
79 |
+
"type": "library",
|
80 |
+
"extra": {
|
81 |
+
"branch-alias": {
|
82 |
+
"dev-master": "3.7-dev"
|
83 |
+
}
|
84 |
+
},
|
85 |
+
"installation-source": "dist",
|
86 |
+
"autoload": {
|
87 |
+
"psr-0": {
|
88 |
+
"Guzzle\\Common": ""
|
89 |
+
}
|
90 |
+
},
|
91 |
+
"notification-url": "https://packagist.org/downloads/",
|
92 |
+
"license": [
|
93 |
+
"MIT"
|
94 |
+
],
|
95 |
+
"description": "Common libraries used by Guzzle",
|
96 |
+
"homepage": "http://guzzlephp.org/",
|
97 |
+
"keywords": [
|
98 |
+
"collection",
|
99 |
+
"common",
|
100 |
+
"event",
|
101 |
+
"exception"
|
102 |
+
]
|
103 |
+
},
|
104 |
+
{
|
105 |
+
"name": "guzzle/stream",
|
106 |
+
"version": "v3.7.4",
|
107 |
+
"version_normalized": "3.7.4.0",
|
108 |
+
"target-dir": "Guzzle/Stream",
|
109 |
+
"source": {
|
110 |
+
"type": "git",
|
111 |
+
"url": "https://github.com/guzzle/stream.git",
|
112 |
+
"reference": "a86111d9ac7db31d65a053c825869409fe8fc83f"
|
113 |
+
},
|
114 |
+
"dist": {
|
115 |
+
"type": "zip",
|
116 |
+
"url": "https://api.github.com/repos/guzzle/stream/zipball/a86111d9ac7db31d65a053c825869409fe8fc83f",
|
117 |
+
"reference": "a86111d9ac7db31d65a053c825869409fe8fc83f",
|
118 |
+
"shasum": ""
|
119 |
+
},
|
120 |
+
"require": {
|
121 |
+
"guzzle/common": "self.version",
|
122 |
+
"php": ">=5.3.2"
|
123 |
+
},
|
124 |
+
"suggest": {
|
125 |
+
"guzzle/http": "To convert Guzzle request objects to PHP streams"
|
126 |
+
},
|
127 |
+
"time": "2013-07-30 22:07:23",
|
128 |
+
"type": "library",
|
129 |
+
"extra": {
|
130 |
+
"branch-alias": {
|
131 |
+
"dev-master": "3.7-dev"
|
132 |
+
}
|
133 |
+
},
|
134 |
+
"installation-source": "dist",
|
135 |
+
"autoload": {
|
136 |
+
"psr-0": {
|
137 |
+
"Guzzle\\Stream": ""
|
138 |
+
}
|
139 |
+
},
|
140 |
+
"notification-url": "https://packagist.org/downloads/",
|
141 |
+
"license": [
|
142 |
+
"MIT"
|
143 |
+
],
|
144 |
+
"authors": [
|
145 |
+
{
|
146 |
+
"name": "Michael Dowling",
|
147 |
+
"email": "mtdowling@gmail.com",
|
148 |
+
"homepage": "https://github.com/mtdowling"
|
149 |
+
}
|
150 |
+
],
|
151 |
+
"description": "Guzzle stream wrapper component",
|
152 |
+
"homepage": "http://guzzlephp.org/",
|
153 |
+
"keywords": [
|
154 |
+
"Guzzle",
|
155 |
+
"component",
|
156 |
+
"stream"
|
157 |
+
]
|
158 |
+
},
|
159 |
+
{
|
160 |
+
"name": "guzzle/parser",
|
161 |
+
"version": "v3.7.4",
|
162 |
+
"version_normalized": "3.7.4.0",
|
163 |
+
"target-dir": "Guzzle/Parser",
|
164 |
+
"source": {
|
165 |
+
"type": "git",
|
166 |
+
"url": "https://github.com/guzzle/parser.git",
|
167 |
+
"reference": "a25c2ddda1c52fb69a4ee56eb530b13ddd9573c2"
|
168 |
+
},
|
169 |
+
"dist": {
|
170 |
+
"type": "zip",
|
171 |
+
"url": "https://api.github.com/repos/guzzle/parser/zipball/a25c2ddda1c52fb69a4ee56eb530b13ddd9573c2",
|
172 |
+
"reference": "a25c2ddda1c52fb69a4ee56eb530b13ddd9573c2",
|
173 |
+
"shasum": ""
|
174 |
+
},
|
175 |
+
"require": {
|
176 |
+
"php": ">=5.3.2"
|
177 |
+
},
|
178 |
+
"time": "2013-07-11 22:46:03",
|
179 |
+
"type": "library",
|
180 |
+
"extra": {
|
181 |
+
"branch-alias": {
|
182 |
+
"dev-master": "3.7-dev"
|
183 |
+
}
|
184 |
+
},
|
185 |
+
"installation-source": "dist",
|
186 |
+
"autoload": {
|
187 |
+
"psr-0": {
|
188 |
+
"Guzzle\\Parser": ""
|
189 |
+
}
|
190 |
+
},
|
191 |
+
"notification-url": "https://packagist.org/downloads/",
|
192 |
+
"license": [
|
193 |
+
"MIT"
|
194 |
+
],
|
195 |
+
"description": "Interchangeable parsers used by Guzzle",
|
196 |
+
"homepage": "http://guzzlephp.org/",
|
197 |
+
"keywords": [
|
198 |
+
"URI Template",
|
199 |
+
"cookie",
|
200 |
+
"http",
|
201 |
+
"message",
|
202 |
+
"url"
|
203 |
+
]
|
204 |
+
},
|
205 |
+
{
|
206 |
+
"name": "guzzle/http",
|
207 |
+
"version": "v3.7.4",
|
208 |
+
"version_normalized": "3.7.4.0",
|
209 |
+
"target-dir": "Guzzle/Http",
|
210 |
+
"source": {
|
211 |
+
"type": "git",
|
212 |
+
"url": "https://github.com/guzzle/http.git",
|
213 |
+
"reference": "3420035adcf312d62a2e64f3e6b3e3e590121786"
|
214 |
+
},
|
215 |
+
"dist": {
|
216 |
+
"type": "zip",
|
217 |
+
"url": "https://api.github.com/repos/guzzle/http/zipball/3420035adcf312d62a2e64f3e6b3e3e590121786",
|
218 |
+
"reference": "3420035adcf312d62a2e64f3e6b3e3e590121786",
|
219 |
+
"shasum": ""
|
220 |
+
},
|
221 |
+
"require": {
|
222 |
+
"guzzle/common": "self.version",
|
223 |
+
"guzzle/parser": "self.version",
|
224 |
+
"guzzle/stream": "self.version",
|
225 |
+
"php": ">=5.3.2"
|
226 |
+
},
|
227 |
+
"suggest": {
|
228 |
+
"ext-curl": "*"
|
229 |
+
},
|
230 |
+
"time": "2013-09-20 22:05:53",
|
231 |
+
"type": "library",
|
232 |
+
"extra": {
|
233 |
+
"branch-alias": {
|
234 |
+
"dev-master": "3.7-dev"
|
235 |
+
}
|
236 |
+
},
|
237 |
+
"installation-source": "dist",
|
238 |
+
"autoload": {
|
239 |
+
"psr-0": {
|
240 |
+
"Guzzle\\Http": ""
|
241 |
+
}
|
242 |
+
},
|
243 |
+
"notification-url": "https://packagist.org/downloads/",
|
244 |
+
"license": [
|
245 |
+
"MIT"
|
246 |
+
],
|
247 |
+
"authors": [
|
248 |
+
{
|
249 |
+
"name": "Michael Dowling",
|
250 |
+
"email": "mtdowling@gmail.com",
|
251 |
+
"homepage": "https://github.com/mtdowling"
|
252 |
+
}
|
253 |
+
],
|
254 |
+
"description": "HTTP libraries used by Guzzle",
|
255 |
+
"homepage": "http://guzzlephp.org/",
|
256 |
+
"keywords": [
|
257 |
+
"Guzzle",
|
258 |
+
"client",
|
259 |
+
"curl",
|
260 |
+
"http",
|
261 |
+
"http client"
|
262 |
+
]
|
263 |
+
},
|
264 |
+
{
|
265 |
+
"name": "rackspace/php-opencloud",
|
266 |
+
"version": "dev-master",
|
267 |
+
"version_normalized": "9999999-dev",
|
268 |
+
"source": {
|
269 |
+
"type": "git",
|
270 |
+
"url": "https://github.com/rackspace/php-opencloud.git",
|
271 |
+
"reference": "bc80ef6bf81e2a5d02851a5b1e2cae2da8ec904e"
|
272 |
+
},
|
273 |
+
"dist": {
|
274 |
+
"type": "zip",
|
275 |
+
"url": "https://api.github.com/repos/rackspace/php-opencloud/zipball/bc80ef6bf81e2a5d02851a5b1e2cae2da8ec904e",
|
276 |
+
"reference": "bc80ef6bf81e2a5d02851a5b1e2cae2da8ec904e",
|
277 |
+
"shasum": ""
|
278 |
+
},
|
279 |
+
"require": {
|
280 |
+
"guzzle/http": "3.7.*@dev",
|
281 |
+
"php": ">=5.3.3"
|
282 |
+
},
|
283 |
+
"require-dev": {
|
284 |
+
"guzzle/plugin-mock": "3.7.*@dev"
|
285 |
+
},
|
286 |
+
"time": "2013-11-29 19:57:37",
|
287 |
+
"type": "library",
|
288 |
+
"installation-source": "source",
|
289 |
+
"autoload": {
|
290 |
+
"psr-0": {
|
291 |
+
"OpenCloud": [
|
292 |
+
"lib/",
|
293 |
+
"tests/"
|
294 |
+
]
|
295 |
+
}
|
296 |
+
},
|
297 |
+
"notification-url": "https://packagist.org/downloads/",
|
298 |
+
"license": [
|
299 |
+
"MIT"
|
300 |
+
],
|
301 |
+
"authors": [
|
302 |
+
{
|
303 |
+
"name": "Glen Campbell",
|
304 |
+
"email": "glen.campbell@rackspace.com"
|
305 |
+
},
|
306 |
+
{
|
307 |
+
"name": "Jamie Hannaford",
|
308 |
+
"email": "jamie.hannaford@rackspace.com",
|
309 |
+
"homepage": "https://github.com/jamiehannaford"
|
310 |
+
}
|
311 |
+
],
|
312 |
+
"description": "PHP SDK for Rackspace/OpenStack APIs",
|
313 |
+
"keywords": [
|
314 |
+
"Openstack",
|
315 |
+
"nova",
|
316 |
+
"opencloud",
|
317 |
+
"rackspace",
|
318 |
+
"swift"
|
319 |
+
]
|
320 |
+
}
|
321 |
+
]
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common;
|
4 |
+
|
5 |
+
use Symfony\Component\EventDispatcher\EventDispatcher;
|
6 |
+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
7 |
+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class that holds an event dispatcher
|
11 |
+
*/
|
12 |
+
class AbstractHasDispatcher implements HasDispatcherInterface
|
13 |
+
{
|
14 |
+
/** @var EventDispatcherInterface */
|
15 |
+
protected $eventDispatcher;
|
16 |
+
|
17 |
+
public static function getAllEvents()
|
18 |
+
{
|
19 |
+
return array();
|
20 |
+
}
|
21 |
+
|
22 |
+
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
|
23 |
+
{
|
24 |
+
$this->eventDispatcher = $eventDispatcher;
|
25 |
+
|
26 |
+
return $this;
|
27 |
+
}
|
28 |
+
|
29 |
+
public function getEventDispatcher()
|
30 |
+
{
|
31 |
+
if (!$this->eventDispatcher) {
|
32 |
+
$this->eventDispatcher = new EventDispatcher();
|
33 |
+
}
|
34 |
+
|
35 |
+
return $this->eventDispatcher;
|
36 |
+
}
|
37 |
+
|
38 |
+
public function dispatch($eventName, array $context = array())
|
39 |
+
{
|
40 |
+
return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
|
41 |
+
}
|
42 |
+
|
43 |
+
public function addSubscriber(EventSubscriberInterface $subscriber)
|
44 |
+
{
|
45 |
+
$this->getEventDispatcher()->addSubscriber($subscriber);
|
46 |
+
|
47 |
+
return $this;
|
48 |
+
}
|
49 |
+
}
|
@@ -0,0 +1,403 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common;
|
4 |
+
|
5 |
+
use Guzzle\Common\Exception\InvalidArgumentException;
|
6 |
+
use Guzzle\Common\Exception\RuntimeException;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Key value pair collection object
|
10 |
+
*/
|
11 |
+
class Collection implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface
|
12 |
+
{
|
13 |
+
/** @var array Data associated with the object. */
|
14 |
+
protected $data;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @param array $data Associative array of data to set
|
18 |
+
*/
|
19 |
+
public function __construct(array $data = array())
|
20 |
+
{
|
21 |
+
$this->data = $data;
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Create a new collection from an array, validate the keys, and add default values where missing
|
26 |
+
*
|
27 |
+
* @param array $config Configuration values to apply.
|
28 |
+
* @param array $defaults Default parameters
|
29 |
+
* @param array $required Required parameter names
|
30 |
+
*
|
31 |
+
* @return self
|
32 |
+
* @throws InvalidArgumentException if a parameter is missing
|
33 |
+
*/
|
34 |
+
public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array())
|
35 |
+
{
|
36 |
+
$data = $config + $defaults;
|
37 |
+
|
38 |
+
if ($missing = array_diff($required, array_keys($data))) {
|
39 |
+
throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing));
|
40 |
+
}
|
41 |
+
|
42 |
+
return new self($data);
|
43 |
+
}
|
44 |
+
|
45 |
+
public function count()
|
46 |
+
{
|
47 |
+
return count($this->data);
|
48 |
+
}
|
49 |
+
|
50 |
+
public function getIterator()
|
51 |
+
{
|
52 |
+
return new \ArrayIterator($this->data);
|
53 |
+
}
|
54 |
+
|
55 |
+
public function toArray()
|
56 |
+
{
|
57 |
+
return $this->data;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Removes all key value pairs
|
62 |
+
*
|
63 |
+
* @return Collection
|
64 |
+
*/
|
65 |
+
public function clear()
|
66 |
+
{
|
67 |
+
$this->data = array();
|
68 |
+
|
69 |
+
return $this;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Get all or a subset of matching key value pairs
|
74 |
+
*
|
75 |
+
* @param array $keys Pass an array of keys to retrieve only a subset of key value pairs
|
76 |
+
*
|
77 |
+
* @return array Returns an array of all matching key value pairs
|
78 |
+
*/
|
79 |
+
public function getAll(array $keys = null)
|
80 |
+
{
|
81 |
+
return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Get a specific key value.
|
86 |
+
*
|
87 |
+
* @param string $key Key to retrieve.
|
88 |
+
*
|
89 |
+
* @return mixed|null Value of the key or NULL
|
90 |
+
*/
|
91 |
+
public function get($key)
|
92 |
+
{
|
93 |
+
return isset($this->data[$key]) ? $this->data[$key] : null;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Set a key value pair
|
98 |
+
*
|
99 |
+
* @param string $key Key to set
|
100 |
+
* @param mixed $value Value to set
|
101 |
+
*
|
102 |
+
* @return Collection Returns a reference to the object
|
103 |
+
*/
|
104 |
+
public function set($key, $value)
|
105 |
+
{
|
106 |
+
$this->data[$key] = $value;
|
107 |
+
|
108 |
+
return $this;
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Add a value to a key. If a key of the same name has already been added, the key value will be converted into an
|
113 |
+
* array and the new value will be pushed to the end of the array.
|
114 |
+
*
|
115 |
+
* @param string $key Key to add
|
116 |
+
* @param mixed $value Value to add to the key
|
117 |
+
*
|
118 |
+
* @return Collection Returns a reference to the object.
|
119 |
+
*/
|
120 |
+
public function add($key, $value)
|
121 |
+
{
|
122 |
+
if (!array_key_exists($key, $this->data)) {
|
123 |
+
$this->data[$key] = $value;
|
124 |
+
} elseif (is_array($this->data[$key])) {
|
125 |
+
$this->data[$key][] = $value;
|
126 |
+
} else {
|
127 |
+
$this->data[$key] = array($this->data[$key], $value);
|
128 |
+
}
|
129 |
+
|
130 |
+
return $this;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Remove a specific key value pair
|
135 |
+
*
|
136 |
+
* @param string $key A key to remove
|
137 |
+
*
|
138 |
+
* @return Collection
|
139 |
+
*/
|
140 |
+
public function remove($key)
|
141 |
+
{
|
142 |
+
unset($this->data[$key]);
|
143 |
+
|
144 |
+
return $this;
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Get all keys in the collection
|
149 |
+
*
|
150 |
+
* @return array
|
151 |
+
*/
|
152 |
+
public function getKeys()
|
153 |
+
{
|
154 |
+
return array_keys($this->data);
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Returns whether or not the specified key is present.
|
159 |
+
*
|
160 |
+
* @param string $key The key for which to check the existence.
|
161 |
+
*
|
162 |
+
* @return bool
|
163 |
+
*/
|
164 |
+
public function hasKey($key)
|
165 |
+
{
|
166 |
+
return array_key_exists($key, $this->data);
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Case insensitive search the keys in the collection
|
171 |
+
*
|
172 |
+
* @param string $key Key to search for
|
173 |
+
*
|
174 |
+
* @return bool|string Returns false if not found, otherwise returns the key
|
175 |
+
*/
|
176 |
+
public function keySearch($key)
|
177 |
+
{
|
178 |
+
foreach (array_keys($this->data) as $k) {
|
179 |
+
if (!strcasecmp($k, $key)) {
|
180 |
+
return $k;
|
181 |
+
}
|
182 |
+
}
|
183 |
+
|
184 |
+
return false;
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Checks if any keys contains a certain value
|
189 |
+
*
|
190 |
+
* @param string $value Value to search for
|
191 |
+
*
|
192 |
+
* @return mixed Returns the key if the value was found FALSE if the value was not found.
|
193 |
+
*/
|
194 |
+
public function hasValue($value)
|
195 |
+
{
|
196 |
+
return array_search($value, $this->data);
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Replace the data of the object with the value of an array
|
201 |
+
*
|
202 |
+
* @param array $data Associative array of data
|
203 |
+
*
|
204 |
+
* @return Collection Returns a reference to the object
|
205 |
+
*/
|
206 |
+
public function replace(array $data)
|
207 |
+
{
|
208 |
+
$this->data = $data;
|
209 |
+
|
210 |
+
return $this;
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Add and merge in a Collection or array of key value pair data.
|
215 |
+
*
|
216 |
+
* @param Collection|array $data Associative array of key value pair data
|
217 |
+
*
|
218 |
+
* @return Collection Returns a reference to the object.
|
219 |
+
*/
|
220 |
+
public function merge($data)
|
221 |
+
{
|
222 |
+
foreach ($data as $key => $value) {
|
223 |
+
$this->add($key, $value);
|
224 |
+
}
|
225 |
+
|
226 |
+
return $this;
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Over write key value pairs in this collection with all of the data from an array or collection.
|
231 |
+
*
|
232 |
+
* @param array|\Traversable $data Values to override over this config
|
233 |
+
*
|
234 |
+
* @return self
|
235 |
+
*/
|
236 |
+
public function overwriteWith($data)
|
237 |
+
{
|
238 |
+
if (is_array($data)) {
|
239 |
+
$this->data = $data + $this->data;
|
240 |
+
} elseif ($data instanceof Collection) {
|
241 |
+
$this->data = $data->toArray() + $this->data;
|
242 |
+
} else {
|
243 |
+
foreach ($data as $key => $value) {
|
244 |
+
$this->data[$key] = $value;
|
245 |
+
}
|
246 |
+
}
|
247 |
+
|
248 |
+
return $this;
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* Returns a Collection containing all the elements of the collection after applying the callback function to each
|
253 |
+
* one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a
|
254 |
+
* modified value
|
255 |
+
*
|
256 |
+
* @param \Closure $closure Closure to apply
|
257 |
+
* @param array $context Context to pass to the closure
|
258 |
+
* @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection
|
259 |
+
*
|
260 |
+
* @return Collection
|
261 |
+
*/
|
262 |
+
public function map(\Closure $closure, array $context = array(), $static = true)
|
263 |
+
{
|
264 |
+
$collection = $static ? new static() : new self();
|
265 |
+
foreach ($this as $key => $value) {
|
266 |
+
$collection->add($key, $closure($key, $value, $context));
|
267 |
+
}
|
268 |
+
|
269 |
+
return $collection;
|
270 |
+
}
|
271 |
+
|
272 |
+
/**
|
273 |
+
* Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns
|
274 |
+
* true, the current value from input is returned into the result Collection. The Closure must accept three
|
275 |
+
* parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value.
|
276 |
+
*
|
277 |
+
* @param \Closure $closure Closure evaluation function
|
278 |
+
* @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection
|
279 |
+
*
|
280 |
+
* @return Collection
|
281 |
+
*/
|
282 |
+
public function filter(\Closure $closure, $static = true)
|
283 |
+
{
|
284 |
+
$collection = ($static) ? new static() : new self();
|
285 |
+
foreach ($this->data as $key => $value) {
|
286 |
+
if ($closure($key, $value)) {
|
287 |
+
$collection->add($key, $value);
|
288 |
+
}
|
289 |
+
}
|
290 |
+
|
291 |
+
return $collection;
|
292 |
+
}
|
293 |
+
|
294 |
+
public function offsetExists($offset)
|
295 |
+
{
|
296 |
+
return isset($this->data[$offset]);
|
297 |
+
}
|
298 |
+
|
299 |
+
public function offsetGet($offset)
|
300 |
+
{
|
301 |
+
return isset($this->data[$offset]) ? $this->data[$offset] : null;
|
302 |
+
}
|
303 |
+
|
304 |
+
public function offsetSet($offset, $value)
|
305 |
+
{
|
306 |
+
$this->data[$offset] = $value;
|
307 |
+
}
|
308 |
+
|
309 |
+
public function offsetUnset($offset)
|
310 |
+
{
|
311 |
+
unset($this->data[$offset]);
|
312 |
+
}
|
313 |
+
|
314 |
+
/**
|
315 |
+
* Set a value into a nested array key. Keys will be created as needed to set the value.
|
316 |
+
*
|
317 |
+
* @param string $path Path to set
|
318 |
+
* @param mixed $value Value to set at the key
|
319 |
+
*
|
320 |
+
* @return self
|
321 |
+
* @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value
|
322 |
+
*/
|
323 |
+
public function setPath($path, $value)
|
324 |
+
{
|
325 |
+
$current =& $this->data;
|
326 |
+
$queue = explode('/', $path);
|
327 |
+
while (null !== ($key = array_shift($queue))) {
|
328 |
+
if (!is_array($current)) {
|
329 |
+
throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array");
|
330 |
+
} elseif (!$queue) {
|
331 |
+
$current[$key] = $value;
|
332 |
+
} elseif (isset($current[$key])) {
|
333 |
+
$current =& $current[$key];
|
334 |
+
} else {
|
335 |
+
$current[$key] = array();
|
336 |
+
$current =& $current[$key];
|
337 |
+
}
|
338 |
+
}
|
339 |
+
|
340 |
+
return $this;
|
341 |
+
}
|
342 |
+
|
343 |
+
/**
|
344 |
+
* Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays)
|
345 |
+
* Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This
|
346 |
+
* can be useful for accepting any key of a sub-array and combining matching keys from each diverging path.
|
347 |
+
*
|
348 |
+
* @param string $path Path to traverse and retrieve a value from
|
349 |
+
* @param string $separator Character used to add depth to the search
|
350 |
+
* @param mixed $data Optional data to descend into (used when wildcards are encountered)
|
351 |
+
*
|
352 |
+
* @return mixed|null
|
353 |
+
*/
|
354 |
+
public function getPath($path, $separator = '/', $data = null)
|
355 |
+
{
|
356 |
+
if ($data === null) {
|
357 |
+
$data =& $this->data;
|
358 |
+
}
|
359 |
+
|
360 |
+
$path = is_array($path) ? $path : explode($separator, $path);
|
361 |
+
while (null !== ($part = array_shift($path))) {
|
362 |
+
if (!is_array($data)) {
|
363 |
+
return null;
|
364 |
+
} elseif (isset($data[$part])) {
|
365 |
+
$data =& $data[$part];
|
366 |
+
} elseif ($part != '*') {
|
367 |
+
return null;
|
368 |
+
} else {
|
369 |
+
// Perform a wildcard search by diverging and merging paths
|
370 |
+
$result = array();
|
371 |
+
foreach ($data as $value) {
|
372 |
+
if (!$path) {
|
373 |
+
$result = array_merge_recursive($result, (array) $value);
|
374 |
+
} elseif (null !== ($test = $this->getPath($path, $separator, $value))) {
|
375 |
+
$result = array_merge_recursive($result, (array) $test);
|
376 |
+
}
|
377 |
+
}
|
378 |
+
return $result;
|
379 |
+
}
|
380 |
+
}
|
381 |
+
|
382 |
+
return $data;
|
383 |
+
}
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Inject configuration settings into an input string
|
387 |
+
*
|
388 |
+
* @param string $input Input to inject
|
389 |
+
*
|
390 |
+
* @return string
|
391 |
+
* @deprecated
|
392 |
+
*/
|
393 |
+
public function inject($input)
|
394 |
+
{
|
395 |
+
Version::warn(__METHOD__ . ' is deprecated');
|
396 |
+
$replace = array();
|
397 |
+
foreach ($this->data as $key => $val) {
|
398 |
+
$replace['{' . $key . '}'] = $val;
|
399 |
+
}
|
400 |
+
|
401 |
+
return strtr($input, $replace);
|
402 |
+
}
|
403 |
+
}
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common;
|
4 |
+
|
5 |
+
use Symfony\Component\EventDispatcher\Event as SymfonyEvent;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Default event for Guzzle notifications
|
9 |
+
*/
|
10 |
+
class Event extends SymfonyEvent implements ToArrayInterface, \ArrayAccess, \IteratorAggregate
|
11 |
+
{
|
12 |
+
/** @var array */
|
13 |
+
private $context;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param array $context Contextual information
|
17 |
+
*/
|
18 |
+
public function __construct(array $context = array())
|
19 |
+
{
|
20 |
+
$this->context = $context;
|
21 |
+
}
|
22 |
+
|
23 |
+
public function getIterator()
|
24 |
+
{
|
25 |
+
return new \ArrayIterator($this->context);
|
26 |
+
}
|
27 |
+
|
28 |
+
public function offsetGet($offset)
|
29 |
+
{
|
30 |
+
return isset($this->context[$offset]) ? $this->context[$offset] : null;
|
31 |
+
}
|
32 |
+
|
33 |
+
public function offsetSet($offset, $value)
|
34 |
+
{
|
35 |
+
$this->context[$offset] = $value;
|
36 |
+
}
|
37 |
+
|
38 |
+
public function offsetExists($offset)
|
39 |
+
{
|
40 |
+
return isset($this->context[$offset]);
|
41 |
+
}
|
42 |
+
|
43 |
+
public function offsetUnset($offset)
|
44 |
+
{
|
45 |
+
unset($this->context[$offset]);
|
46 |
+
}
|
47 |
+
|
48 |
+
public function toArray()
|
49 |
+
{
|
50 |
+
return $this->context;
|
51 |
+
}
|
52 |
+
}
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common\Exception;
|
4 |
+
|
5 |
+
class BadMethodCallException extends \BadMethodCallException implements GuzzleException {}
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common\Exception;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Collection of exceptions
|
7 |
+
*/
|
8 |
+
class ExceptionCollection extends \Exception implements GuzzleException, \IteratorAggregate, \Countable
|
9 |
+
{
|
10 |
+
/** @var array Array of Exceptions */
|
11 |
+
protected $exceptions = array();
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Set all of the exceptions
|
15 |
+
*
|
16 |
+
* @param array $exceptions Array of exceptions
|
17 |
+
*
|
18 |
+
* @return self
|
19 |
+
*/
|
20 |
+
public function setExceptions(array $exceptions)
|
21 |
+
{
|
22 |
+
$this->exceptions = array();
|
23 |
+
foreach ($exceptions as $exception) {
|
24 |
+
$this->add($exception);
|
25 |
+
}
|
26 |
+
|
27 |
+
return $this;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Add exceptions to the collection
|
32 |
+
*
|
33 |
+
* @param ExceptionCollection|\Exception $e Exception to add
|
34 |
+
*
|
35 |
+
* @return ExceptionCollection;
|
36 |
+
*/
|
37 |
+
public function add($e)
|
38 |
+
{
|
39 |
+
if ($this->message) {
|
40 |
+
$this->message .= "\n";
|
41 |
+
}
|
42 |
+
|
43 |
+
if ($e instanceof self) {
|
44 |
+
$this->message .= '(' . get_class($e) . ")";
|
45 |
+
foreach (explode("\n", $e->getMessage()) as $message) {
|
46 |
+
$this->message .= "\n {$message}";
|
47 |
+
}
|
48 |
+
} elseif ($e instanceof \Exception) {
|
49 |
+
$this->exceptions[] = $e;
|
50 |
+
$this->message .= '(' . get_class($e) . ') ' . $e->getMessage();
|
51 |
+
}
|
52 |
+
|
53 |
+
return $this;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Get the total number of request exceptions
|
58 |
+
*
|
59 |
+
* @return int
|
60 |
+
*/
|
61 |
+
public function count()
|
62 |
+
{
|
63 |
+
return count($this->exceptions);
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Allows array-like iteration over the request exceptions
|
68 |
+
*
|
69 |
+
* @return \ArrayIterator
|
70 |
+
*/
|
71 |
+
public function getIterator()
|
72 |
+
{
|
73 |
+
return new \ArrayIterator($this->exceptions);
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Get the first exception in the collection
|
78 |
+
*
|
79 |
+
* @return \Exception
|
80 |
+
*/
|
81 |
+
public function getFirst()
|
82 |
+
{
|
83 |
+
return $this->exceptions ? $this->exceptions[0] : null;
|
84 |
+
}
|
85 |
+
}
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common\Exception;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Guzzle exception
|
7 |
+
*/
|
8 |
+
interface GuzzleException {}
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common\Exception;
|
4 |
+
|
5 |
+
class InvalidArgumentException extends \InvalidArgumentException implements GuzzleException {}
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common\Exception;
|
4 |
+
|
5 |
+
class RuntimeException extends \RuntimeException implements GuzzleException {}
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common\Exception;
|
4 |
+
|
5 |
+
class UnexpectedValueException extends \UnexpectedValueException implements GuzzleException {}
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Interfaces that adds a factory method which is used to instantiate a class from an array of configuration options.
|
7 |
+
*/
|
8 |
+
interface FromConfigInterface
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Static factory method used to turn an array or collection of configuration data into an instantiated object.
|
12 |
+
*
|
13 |
+
* @param array|Collection $config Configuration data
|
14 |
+
*
|
15 |
+
* @return FromConfigInterface
|
16 |
+
*/
|
17 |
+
public static function factory($config = array());
|
18 |
+
}
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common;
|
4 |
+
|
5 |
+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
6 |
+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Holds an event dispatcher
|
10 |
+
*/
|
11 |
+
interface HasDispatcherInterface
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Get a list of all of the events emitted from the class
|
15 |
+
*
|
16 |
+
* @return array
|
17 |
+
*/
|
18 |
+
public static function getAllEvents();
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Set the EventDispatcher of the request
|
22 |
+
*
|
23 |
+
* @param EventDispatcherInterface $eventDispatcher
|
24 |
+
*
|
25 |
+
* @return self
|
26 |
+
*/
|
27 |
+
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher);
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Get the EventDispatcher of the request
|
31 |
+
*
|
32 |
+
* @return EventDispatcherInterface
|
33 |
+
*/
|
34 |
+
public function getEventDispatcher();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Helper to dispatch Guzzle events and set the event name on the event
|
38 |
+
*
|
39 |
+
* @param string $eventName Name of the event to dispatch
|
40 |
+
* @param array $context Context of the event
|
41 |
+
*
|
42 |
+
* @return Event Returns the created event object
|
43 |
+
*/
|
44 |
+
public function dispatch($eventName, array $context = array());
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Add an event subscriber to the dispatcher
|
48 |
+
*
|
49 |
+
* @param EventSubscriberInterface $subscriber Event subscriber
|
50 |
+
*
|
51 |
+
* @return self
|
52 |
+
*/
|
53 |
+
public function addSubscriber(EventSubscriberInterface $subscriber);
|
54 |
+
}
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* An object that can be represented as an array
|
7 |
+
*/
|
8 |
+
interface ToArrayInterface
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Get the array representation of an object
|
12 |
+
*
|
13 |
+
* @return array
|
14 |
+
*/
|
15 |
+
public function toArray();
|
16 |
+
}
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Common;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Guzzle version information
|
7 |
+
*/
|
8 |
+
class Version
|
9 |
+
{
|
10 |
+
const VERSION = '3.7.4';
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var bool Set this value to true to enable warnings for deprecated functionality use. This should be on in your
|
14 |
+
* unit tests, but probably not in production.
|
15 |
+
*/
|
16 |
+
public static $emitWarnings = false;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Emit a deprecation warning
|
20 |
+
*
|
21 |
+
* @param string $message Warning message
|
22 |
+
*/
|
23 |
+
public static function warn($message)
|
24 |
+
{
|
25 |
+
if (self::$emitWarnings) {
|
26 |
+
trigger_error('Deprecation warning: ' . $message, E_USER_DEPRECATED);
|
27 |
+
}
|
28 |
+
}
|
29 |
+
}
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "guzzle/common",
|
3 |
+
"homepage": "http://guzzlephp.org/",
|
4 |
+
"description": "Common libraries used by Guzzle",
|
5 |
+
"keywords": ["common", "event", "exception", "collection"],
|
6 |
+
"license": "MIT",
|
7 |
+
"require": {
|
8 |
+
"php": ">=5.3.2",
|
9 |
+
"symfony/event-dispatcher": ">=2.1"
|
10 |
+
},
|
11 |
+
"autoload": {
|
12 |
+
"psr-0": { "Guzzle\\Common": "" }
|
13 |
+
},
|
14 |
+
"target-dir": "Guzzle/Common",
|
15 |
+
"extra": {
|
16 |
+
"branch-alias": {
|
17 |
+
"dev-master": "3.7-dev"
|
18 |
+
}
|
19 |
+
}
|
20 |
+
}
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http;
|
4 |
+
|
5 |
+
use Guzzle\Stream\Stream;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Abstract decorator used to wrap entity bodies
|
9 |
+
*/
|
10 |
+
class AbstractEntityBodyDecorator implements EntityBodyInterface
|
11 |
+
{
|
12 |
+
/** @var EntityBodyInterface Decorated entity body */
|
13 |
+
protected $body;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param EntityBodyInterface $body Entity body to decorate
|
17 |
+
*/
|
18 |
+
public function __construct(EntityBodyInterface $body)
|
19 |
+
{
|
20 |
+
$this->body = $body;
|
21 |
+
}
|
22 |
+
|
23 |
+
public function __toString()
|
24 |
+
{
|
25 |
+
return (string) $this->body;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Allow decorators to implement custom methods
|
30 |
+
*
|
31 |
+
* @param string $method Missing method name
|
32 |
+
* @param array $args Method arguments
|
33 |
+
*
|
34 |
+
* @return mixed
|
35 |
+
*/
|
36 |
+
public function __call($method, array $args)
|
37 |
+
{
|
38 |
+
return call_user_func_array(array($this->body, $method), $args);
|
39 |
+
}
|
40 |
+
|
41 |
+
public function close()
|
42 |
+
{
|
43 |
+
return $this->body->close();
|
44 |
+
}
|
45 |
+
|
46 |
+
public function setRewindFunction($callable)
|
47 |
+
{
|
48 |
+
$this->body->setRewindFunction($callable);
|
49 |
+
|
50 |
+
return $this;
|
51 |
+
}
|
52 |
+
|
53 |
+
public function rewind()
|
54 |
+
{
|
55 |
+
return $this->body->rewind();
|
56 |
+
}
|
57 |
+
|
58 |
+
public function compress($filter = 'zlib.deflate')
|
59 |
+
{
|
60 |
+
return $this->body->compress($filter);
|
61 |
+
}
|
62 |
+
|
63 |
+
public function uncompress($filter = 'zlib.inflate')
|
64 |
+
{
|
65 |
+
return $this->body->uncompress($filter);
|
66 |
+
}
|
67 |
+
|
68 |
+
public function getContentLength()
|
69 |
+
{
|
70 |
+
return $this->getSize();
|
71 |
+
}
|
72 |
+
|
73 |
+
public function getContentType()
|
74 |
+
{
|
75 |
+
return $this->body->getContentType();
|
76 |
+
}
|
77 |
+
|
78 |
+
public function getContentMd5($rawOutput = false, $base64Encode = false)
|
79 |
+
{
|
80 |
+
$hash = Stream::getHash($this, 'md5', $rawOutput);
|
81 |
+
|
82 |
+
return $hash && $base64Encode ? base64_encode($hash) : $hash;
|
83 |
+
}
|
84 |
+
|
85 |
+
public function getContentEncoding()
|
86 |
+
{
|
87 |
+
return $this->body->getContentEncoding();
|
88 |
+
}
|
89 |
+
|
90 |
+
public function getMetaData($key = null)
|
91 |
+
{
|
92 |
+
return $this->body->getMetaData($key);
|
93 |
+
}
|
94 |
+
|
95 |
+
public function getStream()
|
96 |
+
{
|
97 |
+
return $this->body->getStream();
|
98 |
+
}
|
99 |
+
|
100 |
+
public function setStream($stream, $size = 0)
|
101 |
+
{
|
102 |
+
$this->body->setStream($stream, $size);
|
103 |
+
|
104 |
+
return $this;
|
105 |
+
}
|
106 |
+
|
107 |
+
public function detachStream()
|
108 |
+
{
|
109 |
+
$this->body->detachStream();
|
110 |
+
|
111 |
+
return $this;
|
112 |
+
}
|
113 |
+
|
114 |
+
public function getWrapper()
|
115 |
+
{
|
116 |
+
return $this->body->getWrapper();
|
117 |
+
}
|
118 |
+
|
119 |
+
public function getWrapperData()
|
120 |
+
{
|
121 |
+
return $this->body->getWrapperData();
|
122 |
+
}
|
123 |
+
|
124 |
+
public function getStreamType()
|
125 |
+
{
|
126 |
+
return $this->body->getStreamType();
|
127 |
+
}
|
128 |
+
|
129 |
+
public function getUri()
|
130 |
+
{
|
131 |
+
return $this->body->getUri();
|
132 |
+
}
|
133 |
+
|
134 |
+
public function getSize()
|
135 |
+
{
|
136 |
+
return $this->body->getSize();
|
137 |
+
}
|
138 |
+
|
139 |
+
public function isReadable()
|
140 |
+
{
|
141 |
+
return $this->body->isReadable();
|
142 |
+
}
|
143 |
+
|
144 |
+
public function isRepeatable()
|
145 |
+
{
|
146 |
+
return $this->isSeekable() && $this->isReadable();
|
147 |
+
}
|
148 |
+
|
149 |
+
public function isWritable()
|
150 |
+
{
|
151 |
+
return $this->body->isWritable();
|
152 |
+
}
|
153 |
+
|
154 |
+
public function isConsumed()
|
155 |
+
{
|
156 |
+
return $this->body->isConsumed();
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Alias of isConsumed()
|
161 |
+
* {@inheritdoc}
|
162 |
+
*/
|
163 |
+
public function feof()
|
164 |
+
{
|
165 |
+
return $this->isConsumed();
|
166 |
+
}
|
167 |
+
|
168 |
+
public function isLocal()
|
169 |
+
{
|
170 |
+
return $this->body->isLocal();
|
171 |
+
}
|
172 |
+
|
173 |
+
public function isSeekable()
|
174 |
+
{
|
175 |
+
return $this->body->isSeekable();
|
176 |
+
}
|
177 |
+
|
178 |
+
public function setSize($size)
|
179 |
+
{
|
180 |
+
$this->body->setSize($size);
|
181 |
+
|
182 |
+
return $this;
|
183 |
+
}
|
184 |
+
|
185 |
+
public function seek($offset, $whence = SEEK_SET)
|
186 |
+
{
|
187 |
+
return $this->body->seek($offset, $whence);
|
188 |
+
}
|
189 |
+
|
190 |
+
public function read($length)
|
191 |
+
{
|
192 |
+
return $this->body->read($length);
|
193 |
+
}
|
194 |
+
|
195 |
+
public function write($string)
|
196 |
+
{
|
197 |
+
return $this->body->write($string);
|
198 |
+
}
|
199 |
+
|
200 |
+
public function readLine($maxLength = null)
|
201 |
+
{
|
202 |
+
return $this->body->readLine($maxLength);
|
203 |
+
}
|
204 |
+
|
205 |
+
public function ftell()
|
206 |
+
{
|
207 |
+
return $this->body->ftell();
|
208 |
+
}
|
209 |
+
|
210 |
+
public function getCustomData($key)
|
211 |
+
{
|
212 |
+
return $this->body->getCustomData($key);
|
213 |
+
}
|
214 |
+
|
215 |
+
public function setCustomData($key, $value)
|
216 |
+
{
|
217 |
+
$this->body->setCustomData($key, $value);
|
218 |
+
|
219 |
+
return $this;
|
220 |
+
}
|
221 |
+
}
|
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http;
|
4 |
+
|
5 |
+
use Guzzle\Common\Exception\RuntimeException;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* EntityBody decorator that can cache previously read bytes from a sequentially read tstream
|
9 |
+
*/
|
10 |
+
class CachingEntityBody extends AbstractEntityBodyDecorator
|
11 |
+
{
|
12 |
+
/** @var EntityBody Remote stream used to actually pull data onto the buffer */
|
13 |
+
protected $remoteStream;
|
14 |
+
|
15 |
+
/** @var int The number of bytes to skip reading due to a write on the temporary buffer */
|
16 |
+
protected $skipReadBytes = 0;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* We will treat the buffer object as the body of the entity body
|
20 |
+
* {@inheritdoc}
|
21 |
+
*/
|
22 |
+
public function __construct(EntityBodyInterface $body)
|
23 |
+
{
|
24 |
+
$this->remoteStream = $body;
|
25 |
+
$this->body = new EntityBody(fopen('php://temp', 'r+'));
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Will give the contents of the buffer followed by the exhausted remote stream.
|
30 |
+
*
|
31 |
+
* Warning: Loads the entire stream into memory
|
32 |
+
*
|
33 |
+
* @return string
|
34 |
+
*/
|
35 |
+
public function __toString()
|
36 |
+
{
|
37 |
+
$pos = $this->ftell();
|
38 |
+
$this->rewind();
|
39 |
+
|
40 |
+
$str = '';
|
41 |
+
while (!$this->isConsumed()) {
|
42 |
+
$str .= $this->read(16384);
|
43 |
+
}
|
44 |
+
|
45 |
+
$this->seek($pos);
|
46 |
+
|
47 |
+
return $str;
|
48 |
+
}
|
49 |
+
|
50 |
+
public function getSize()
|
51 |
+
{
|
52 |
+
return max($this->body->getSize(), $this->remoteStream->getSize());
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* {@inheritdoc}
|
57 |
+
* @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream
|
58 |
+
*/
|
59 |
+
public function seek($offset, $whence = SEEK_SET)
|
60 |
+
{
|
61 |
+
if ($whence == SEEK_SET) {
|
62 |
+
$byte = $offset;
|
63 |
+
} elseif ($whence == SEEK_CUR) {
|
64 |
+
$byte = $offset + $this->ftell();
|
65 |
+
} else {
|
66 |
+
throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations');
|
67 |
+
}
|
68 |
+
|
69 |
+
// You cannot skip ahead past where you've read from the remote stream
|
70 |
+
if ($byte > $this->body->getSize()) {
|
71 |
+
throw new RuntimeException(
|
72 |
+
"Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes"
|
73 |
+
);
|
74 |
+
}
|
75 |
+
|
76 |
+
return $this->body->seek($byte);
|
77 |
+
}
|
78 |
+
|
79 |
+
public function rewind()
|
80 |
+
{
|
81 |
+
return $this->seek(0);
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Does not support custom rewind functions
|
86 |
+
*
|
87 |
+
* @throws RuntimeException
|
88 |
+
*/
|
89 |
+
public function setRewindFunction($callable)
|
90 |
+
{
|
91 |
+
throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions');
|
92 |
+
}
|
93 |
+
|
94 |
+
public function read($length)
|
95 |
+
{
|
96 |
+
// Perform a regular read on any previously read data from the buffer
|
97 |
+
$data = $this->body->read($length);
|
98 |
+
$remaining = $length - strlen($data);
|
99 |
+
|
100 |
+
// More data was requested so read from the remote stream
|
101 |
+
if ($remaining) {
|
102 |
+
// If data was written to the buffer in a position that would have been filled from the remote stream,
|
103 |
+
// then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This
|
104 |
+
// mimics the behavior of other PHP stream wrappers.
|
105 |
+
$remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes);
|
106 |
+
|
107 |
+
if ($this->skipReadBytes) {
|
108 |
+
$len = strlen($remoteData);
|
109 |
+
$remoteData = substr($remoteData, $this->skipReadBytes);
|
110 |
+
$this->skipReadBytes = max(0, $this->skipReadBytes - $len);
|
111 |
+
}
|
112 |
+
|
113 |
+
$data .= $remoteData;
|
114 |
+
$this->body->write($remoteData);
|
115 |
+
}
|
116 |
+
|
117 |
+
return $data;
|
118 |
+
}
|
119 |
+
|
120 |
+
public function write($string)
|
121 |
+
{
|
122 |
+
// When appending to the end of the currently read stream, you'll want to skip bytes from being read from
|
123 |
+
// the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length.
|
124 |
+
$overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell();
|
125 |
+
if ($overflow > 0) {
|
126 |
+
$this->skipReadBytes += $overflow;
|
127 |
+
}
|
128 |
+
|
129 |
+
return $this->body->write($string);
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* {@inheritdoc}
|
134 |
+
* @link http://php.net/manual/en/function.fgets.php
|
135 |
+
*/
|
136 |
+
public function readLine($maxLength = null)
|
137 |
+
{
|
138 |
+
$buffer = '';
|
139 |
+
$size = 0;
|
140 |
+
while (!$this->isConsumed()) {
|
141 |
+
$byte = $this->read(1);
|
142 |
+
$buffer .= $byte;
|
143 |
+
// Break when a new line is found or the max length - 1 is reached
|
144 |
+
if ($byte == PHP_EOL || ++$size == $maxLength - 1) {
|
145 |
+
break;
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
return $buffer;
|
150 |
+
}
|
151 |
+
|
152 |
+
public function isConsumed()
|
153 |
+
{
|
154 |
+
return $this->body->isConsumed() && $this->remoteStream->isConsumed();
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Close both the remote stream and buffer stream
|
159 |
+
*/
|
160 |
+
public function close()
|
161 |
+
{
|
162 |
+
return $this->remoteStream->close() && $this->body->close();
|
163 |
+
}
|
164 |
+
|
165 |
+
public function setStream($stream, $size = 0)
|
166 |
+
{
|
167 |
+
$this->remoteStream->setStream($stream, $size);
|
168 |
+
}
|
169 |
+
|
170 |
+
public function getContentType()
|
171 |
+
{
|
172 |
+
return $this->remoteStream->getContentType();
|
173 |
+
}
|
174 |
+
|
175 |
+
public function getContentEncoding()
|
176 |
+
{
|
177 |
+
return $this->remoteStream->getContentEncoding();
|
178 |
+
}
|
179 |
+
|
180 |
+
public function getMetaData($key = null)
|
181 |
+
{
|
182 |
+
return $this->remoteStream->getMetaData($key);
|
183 |
+
}
|
184 |
+
|
185 |
+
public function getStream()
|
186 |
+
{
|
187 |
+
return $this->remoteStream->getStream();
|
188 |
+
}
|
189 |
+
|
190 |
+
public function getWrapper()
|
191 |
+
{
|
192 |
+
return $this->remoteStream->getWrapper();
|
193 |
+
}
|
194 |
+
|
195 |
+
public function getWrapperData()
|
196 |
+
{
|
197 |
+
return $this->remoteStream->getWrapperData();
|
198 |
+
}
|
199 |
+
|
200 |
+
public function getStreamType()
|
201 |
+
{
|
202 |
+
return $this->remoteStream->getStreamType();
|
203 |
+
}
|
204 |
+
|
205 |
+
public function getUri()
|
206 |
+
{
|
207 |
+
return $this->remoteStream->getUri();
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Always retrieve custom data from the remote stream
|
212 |
+
* {@inheritdoc}
|
213 |
+
*/
|
214 |
+
public function getCustomData($key)
|
215 |
+
{
|
216 |
+
return $this->remoteStream->getCustomData($key);
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Always set custom data on the remote stream
|
221 |
+
* {@inheritdoc}
|
222 |
+
*/
|
223 |
+
public function setCustomData($key, $value)
|
224 |
+
{
|
225 |
+
$this->remoteStream->setCustomData($key, $value);
|
226 |
+
|
227 |
+
return $this;
|
228 |
+
}
|
229 |
+
}
|
@@ -0,0 +1,506 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http;
|
4 |
+
|
5 |
+
use Guzzle\Common\Collection;
|
6 |
+
use Guzzle\Common\AbstractHasDispatcher;
|
7 |
+
use Guzzle\Common\Exception\ExceptionCollection;
|
8 |
+
use Guzzle\Common\Exception\InvalidArgumentException;
|
9 |
+
use Guzzle\Common\Exception\RuntimeException;
|
10 |
+
use Guzzle\Common\Version;
|
11 |
+
use Guzzle\Parser\ParserRegistry;
|
12 |
+
use Guzzle\Parser\UriTemplate\UriTemplateInterface;
|
13 |
+
use Guzzle\Http\Message\RequestInterface;
|
14 |
+
use Guzzle\Http\Message\RequestFactory;
|
15 |
+
use Guzzle\Http\Message\RequestFactoryInterface;
|
16 |
+
use Guzzle\Http\Curl\CurlMultiInterface;
|
17 |
+
use Guzzle\Http\Curl\CurlMultiProxy;
|
18 |
+
use Guzzle\Http\Curl\CurlHandle;
|
19 |
+
use Guzzle\Http\Curl\CurlVersion;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* HTTP client
|
23 |
+
*/
|
24 |
+
class Client extends AbstractHasDispatcher implements ClientInterface
|
25 |
+
{
|
26 |
+
/** @deprecated Use [request.options][params] */
|
27 |
+
const REQUEST_PARAMS = 'request.params';
|
28 |
+
|
29 |
+
const REQUEST_OPTIONS = 'request.options';
|
30 |
+
const CURL_OPTIONS = 'curl.options';
|
31 |
+
const SSL_CERT_AUTHORITY = 'ssl.certificate_authority';
|
32 |
+
const DISABLE_REDIRECTS = RedirectPlugin::DISABLE;
|
33 |
+
|
34 |
+
/** @var Collection Default HTTP headers to set on each request */
|
35 |
+
protected $defaultHeaders;
|
36 |
+
|
37 |
+
/** @var string The user agent string to set on each request */
|
38 |
+
protected $userAgent;
|
39 |
+
|
40 |
+
/** @var Collection Parameter object holding configuration data */
|
41 |
+
private $config;
|
42 |
+
|
43 |
+
/** @var Url Base URL of the client */
|
44 |
+
private $baseUrl;
|
45 |
+
|
46 |
+
/** @var CurlMultiInterface CurlMulti object used internally */
|
47 |
+
private $curlMulti;
|
48 |
+
|
49 |
+
/** @var UriTemplateInterface URI template owned by the client */
|
50 |
+
private $uriTemplate;
|
51 |
+
|
52 |
+
/** @var RequestFactoryInterface Request factory used by the client */
|
53 |
+
protected $requestFactory;
|
54 |
+
|
55 |
+
public static function getAllEvents()
|
56 |
+
{
|
57 |
+
return array(self::CREATE_REQUEST);
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @param string $baseUrl Base URL of the web service
|
62 |
+
* @param array|Collection $config Configuration settings
|
63 |
+
*
|
64 |
+
* @throws RuntimeException if cURL is not installed
|
65 |
+
*/
|
66 |
+
public function __construct($baseUrl = '', $config = null)
|
67 |
+
{
|
68 |
+
if (!extension_loaded('curl')) {
|
69 |
+
// @codeCoverageIgnoreStart
|
70 |
+
throw new RuntimeException('The PHP cURL extension must be installed to use Guzzle.');
|
71 |
+
// @codeCoverageIgnoreEnd
|
72 |
+
}
|
73 |
+
$this->setConfig($config ?: new Collection());
|
74 |
+
$this->initSsl();
|
75 |
+
$this->setBaseUrl($baseUrl);
|
76 |
+
$this->defaultHeaders = new Collection();
|
77 |
+
$this->setRequestFactory(RequestFactory::getInstance());
|
78 |
+
$this->userAgent = $this->getDefaultUserAgent();
|
79 |
+
if (!$this->config[self::DISABLE_REDIRECTS]) {
|
80 |
+
$this->addSubscriber(new RedirectPlugin());
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
final public function setConfig($config)
|
85 |
+
{
|
86 |
+
if ($config instanceof Collection) {
|
87 |
+
$this->config = $config;
|
88 |
+
} elseif (is_array($config)) {
|
89 |
+
$this->config = new Collection($config);
|
90 |
+
} else {
|
91 |
+
throw new InvalidArgumentException('Config must be an array or Collection');
|
92 |
+
}
|
93 |
+
|
94 |
+
return $this;
|
95 |
+
}
|
96 |
+
|
97 |
+
final public function getConfig($key = false)
|
98 |
+
{
|
99 |
+
return $key ? $this->config[$key] : $this->config;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Set a default request option on the client that will be used as a default for each request
|
104 |
+
*
|
105 |
+
* @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
|
106 |
+
* @param mixed $value Value to set
|
107 |
+
*
|
108 |
+
* @return $this
|
109 |
+
*/
|
110 |
+
public function setDefaultOption($keyOrPath, $value)
|
111 |
+
{
|
112 |
+
$keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
|
113 |
+
$this->config->setPath($keyOrPath, $value);
|
114 |
+
|
115 |
+
return $this;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Retrieve a default request option from the client
|
120 |
+
*
|
121 |
+
* @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
|
122 |
+
*
|
123 |
+
* @return mixed|null
|
124 |
+
*/
|
125 |
+
public function getDefaultOption($keyOrPath)
|
126 |
+
{
|
127 |
+
$keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
|
128 |
+
|
129 |
+
return $this->config->getPath($keyOrPath);
|
130 |
+
}
|
131 |
+
|
132 |
+
final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2)
|
133 |
+
{
|
134 |
+
$opts = $this->config[self::CURL_OPTIONS] ?: array();
|
135 |
+
|
136 |
+
if ($certificateAuthority === true) {
|
137 |
+
// use bundled CA bundle, set secure defaults
|
138 |
+
$opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem';
|
139 |
+
$opts[CURLOPT_SSL_VERIFYPEER] = true;
|
140 |
+
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
|
141 |
+
} elseif ($certificateAuthority === false) {
|
142 |
+
unset($opts[CURLOPT_CAINFO]);
|
143 |
+
$opts[CURLOPT_SSL_VERIFYPEER] = false;
|
144 |
+
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
|
145 |
+
} elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) {
|
146 |
+
throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean');
|
147 |
+
} elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) {
|
148 |
+
throw new InvalidArgumentException('verifyHost must be 0, 1 or 2');
|
149 |
+
} else {
|
150 |
+
$opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer;
|
151 |
+
$opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost;
|
152 |
+
if (is_file($certificateAuthority)) {
|
153 |
+
unset($opts[CURLOPT_CAPATH]);
|
154 |
+
$opts[CURLOPT_CAINFO] = $certificateAuthority;
|
155 |
+
} elseif (is_dir($certificateAuthority)) {
|
156 |
+
unset($opts[CURLOPT_CAINFO]);
|
157 |
+
$opts[CURLOPT_CAPATH] = $certificateAuthority;
|
158 |
+
} else {
|
159 |
+
throw new RuntimeException(
|
160 |
+
'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority
|
161 |
+
);
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
$this->config->set(self::CURL_OPTIONS, $opts);
|
166 |
+
|
167 |
+
return $this;
|
168 |
+
}
|
169 |
+
|
170 |
+
public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array())
|
171 |
+
{
|
172 |
+
if (!$uri) {
|
173 |
+
$url = $this->getBaseUrl();
|
174 |
+
} else {
|
175 |
+
if (!is_array($uri)) {
|
176 |
+
$templateVars = null;
|
177 |
+
} else {
|
178 |
+
list($uri, $templateVars) = $uri;
|
179 |
+
}
|
180 |
+
if (substr($uri, 0, 4) === 'http') {
|
181 |
+
// Use absolute URLs as-is
|
182 |
+
$url = $this->expandTemplate($uri, $templateVars);
|
183 |
+
} else {
|
184 |
+
$url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars));
|
185 |
+
}
|
186 |
+
}
|
187 |
+
|
188 |
+
// If default headers are provided, then merge them under any explicitly provided headers for the request
|
189 |
+
if (count($this->defaultHeaders)) {
|
190 |
+
if (!$headers) {
|
191 |
+
$headers = $this->defaultHeaders->toArray();
|
192 |
+
} elseif (is_array($headers)) {
|
193 |
+
$headers += $this->defaultHeaders->toArray();
|
194 |
+
} elseif ($headers instanceof Collection) {
|
195 |
+
$headers = $headers->toArray() + $this->defaultHeaders->toArray();
|
196 |
+
}
|
197 |
+
}
|
198 |
+
|
199 |
+
return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options);
|
200 |
+
}
|
201 |
+
|
202 |
+
public function getBaseUrl($expand = true)
|
203 |
+
{
|
204 |
+
return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl;
|
205 |
+
}
|
206 |
+
|
207 |
+
public function setBaseUrl($url)
|
208 |
+
{
|
209 |
+
$this->baseUrl = $url;
|
210 |
+
|
211 |
+
return $this;
|
212 |
+
}
|
213 |
+
|
214 |
+
public function setUserAgent($userAgent, $includeDefault = false)
|
215 |
+
{
|
216 |
+
if ($includeDefault) {
|
217 |
+
$userAgent .= ' ' . $this->getDefaultUserAgent();
|
218 |
+
}
|
219 |
+
$this->userAgent = $userAgent;
|
220 |
+
|
221 |
+
return $this;
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Get the default User-Agent string to use with Guzzle
|
226 |
+
*
|
227 |
+
* @return string
|
228 |
+
*/
|
229 |
+
public function getDefaultUserAgent()
|
230 |
+
{
|
231 |
+
return 'Guzzle/' . Version::VERSION
|
232 |
+
. ' curl/' . CurlVersion::getInstance()->get('version')
|
233 |
+
. ' PHP/' . PHP_VERSION;
|
234 |
+
}
|
235 |
+
|
236 |
+
public function get($uri = null, $headers = null, $options = array())
|
237 |
+
{
|
238 |
+
// BC compat: $options can be a string, resource, etc to specify where the response body is downloaded
|
239 |
+
return is_array($options)
|
240 |
+
? $this->createRequest('GET', $uri, $headers, null, $options)
|
241 |
+
: $this->createRequest('GET', $uri, $headers, $options);
|
242 |
+
}
|
243 |
+
|
244 |
+
public function head($uri = null, $headers = null, array $options = array())
|
245 |
+
{
|
246 |
+
return $this->createRequest('HEAD', $uri, $headers, null, $options);
|
247 |
+
}
|
248 |
+
|
249 |
+
public function delete($uri = null, $headers = null, $body = null, array $options = array())
|
250 |
+
{
|
251 |
+
return $this->createRequest('DELETE', $uri, $headers, $body, $options);
|
252 |
+
}
|
253 |
+
|
254 |
+
public function put($uri = null, $headers = null, $body = null, array $options = array())
|
255 |
+
{
|
256 |
+
return $this->createRequest('PUT', $uri, $headers, $body, $options);
|
257 |
+
}
|
258 |
+
|
259 |
+
public function patch($uri = null, $headers = null, $body = null, array $options = array())
|
260 |
+
{
|
261 |
+
return $this->createRequest('PATCH', $uri, $headers, $body, $options);
|
262 |
+
}
|
263 |
+
|
264 |
+
public function post($uri = null, $headers = null, $postBody = null, array $options = array())
|
265 |
+
{
|
266 |
+
return $this->createRequest('POST', $uri, $headers, $postBody, $options);
|
267 |
+
}
|
268 |
+
|
269 |
+
public function options($uri = null, array $options = array())
|
270 |
+
{
|
271 |
+
return $this->createRequest('OPTIONS', $uri, $options);
|
272 |
+
}
|
273 |
+
|
274 |
+
public function send($requests)
|
275 |
+
{
|
276 |
+
if (!($requests instanceof RequestInterface)) {
|
277 |
+
return $this->sendMultiple($requests);
|
278 |
+
}
|
279 |
+
|
280 |
+
try {
|
281 |
+
/** @var $requests RequestInterface */
|
282 |
+
$this->getCurlMulti()->add($requests)->send();
|
283 |
+
return $requests->getResponse();
|
284 |
+
} catch (ExceptionCollection $e) {
|
285 |
+
throw $e->getFirst();
|
286 |
+
}
|
287 |
+
}
|
288 |
+
|
289 |
+
/**
|
290 |
+
* Set a curl multi object to be used internally by the client for transferring requests.
|
291 |
+
*
|
292 |
+
* @param CurlMultiInterface $curlMulti Multi object
|
293 |
+
*
|
294 |
+
* @return self
|
295 |
+
*/
|
296 |
+
public function setCurlMulti(CurlMultiInterface $curlMulti)
|
297 |
+
{
|
298 |
+
$this->curlMulti = $curlMulti;
|
299 |
+
|
300 |
+
return $this;
|
301 |
+
}
|
302 |
+
|
303 |
+
/**
|
304 |
+
* @return CurlMultiInterface|CurlMultiProxy
|
305 |
+
*/
|
306 |
+
public function getCurlMulti()
|
307 |
+
{
|
308 |
+
if (!$this->curlMulti) {
|
309 |
+
$this->curlMulti = new CurlMultiProxy();
|
310 |
+
}
|
311 |
+
|
312 |
+
return $this->curlMulti;
|
313 |
+
}
|
314 |
+
|
315 |
+
public function setRequestFactory(RequestFactoryInterface $factory)
|
316 |
+
{
|
317 |
+
$this->requestFactory = $factory;
|
318 |
+
|
319 |
+
return $this;
|
320 |
+
}
|
321 |
+
|
322 |
+
/**
|
323 |
+
* Set the URI template expander to use with the client
|
324 |
+
*
|
325 |
+
* @param UriTemplateInterface $uriTemplate URI template expander
|
326 |
+
*
|
327 |
+
* @return self
|
328 |
+
*/
|
329 |
+
public function setUriTemplate(UriTemplateInterface $uriTemplate)
|
330 |
+
{
|
331 |
+
$this->uriTemplate = $uriTemplate;
|
332 |
+
|
333 |
+
return $this;
|
334 |
+
}
|
335 |
+
|
336 |
+
/**
|
337 |
+
* Copy the cacert.pem file from the phar if it is not in the temp folder and validate the MD5 checksum
|
338 |
+
*
|
339 |
+
* @param bool $md5Check Set to false to not perform the MD5 validation
|
340 |
+
*
|
341 |
+
* @return string Returns the path to the extracted cacert
|
342 |
+
* @throws RuntimeException if the file cannot be copied or there is a MD5 mismatch
|
343 |
+
*/
|
344 |
+
public function preparePharCacert($md5Check = true)
|
345 |
+
{
|
346 |
+
$from = __DIR__ . '/Resources/cacert.pem';
|
347 |
+
$certFile = sys_get_temp_dir() . '/guzzle-cacert.pem';
|
348 |
+
if (!file_exists($certFile) && !copy($from, $certFile)) {
|
349 |
+
throw new RuntimeException("Could not copy {$from} to {$certFile}: " . var_export(error_get_last(), true));
|
350 |
+
} elseif ($md5Check) {
|
351 |
+
$actualMd5 = md5_file($certFile);
|
352 |
+
$expectedMd5 = trim(file_get_contents("{$from}.md5"));
|
353 |
+
if ($actualMd5 != $expectedMd5) {
|
354 |
+
throw new RuntimeException("{$certFile} MD5 mismatch: expected {$expectedMd5} but got {$actualMd5}");
|
355 |
+
}
|
356 |
+
}
|
357 |
+
|
358 |
+
return $certFile;
|
359 |
+
}
|
360 |
+
|
361 |
+
/**
|
362 |
+
* Expand a URI template while merging client config settings into the template variables
|
363 |
+
*
|
364 |
+
* @param string $template Template to expand
|
365 |
+
* @param array $variables Variables to inject
|
366 |
+
*
|
367 |
+
* @return string
|
368 |
+
*/
|
369 |
+
protected function expandTemplate($template, array $variables = null)
|
370 |
+
{
|
371 |
+
$expansionVars = $this->getConfig()->toArray();
|
372 |
+
if ($variables) {
|
373 |
+
$expansionVars = $variables + $expansionVars;
|
374 |
+
}
|
375 |
+
|
376 |
+
return $this->getUriTemplate()->expand($template, $expansionVars);
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Get the URI template expander used by the client
|
381 |
+
*
|
382 |
+
* @return UriTemplateInterface
|
383 |
+
*/
|
384 |
+
protected function getUriTemplate()
|
385 |
+
{
|
386 |
+
if (!$this->uriTemplate) {
|
387 |
+
$this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template');
|
388 |
+
}
|
389 |
+
|
390 |
+
return $this->uriTemplate;
|
391 |
+
}
|
392 |
+
|
393 |
+
/**
|
394 |
+
* Send multiple requests in parallel
|
395 |
+
*
|
396 |
+
* @param array $requests Array of RequestInterface objects
|
397 |
+
*
|
398 |
+
* @return array Returns an array of Response objects
|
399 |
+
*/
|
400 |
+
protected function sendMultiple(array $requests)
|
401 |
+
{
|
402 |
+
$curlMulti = $this->getCurlMulti();
|
403 |
+
foreach ($requests as $request) {
|
404 |
+
$curlMulti->add($request);
|
405 |
+
}
|
406 |
+
$curlMulti->send();
|
407 |
+
|
408 |
+
/** @var $request RequestInterface */
|
409 |
+
$result = array();
|
410 |
+
foreach ($requests as $request) {
|
411 |
+
$result[] = $request->getResponse();
|
412 |
+
}
|
413 |
+
|
414 |
+
return $result;
|
415 |
+
}
|
416 |
+
|
417 |
+
/**
|
418 |
+
* Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request.
|
419 |
+
*
|
420 |
+
* @param RequestInterface $request Request to prepare for the client
|
421 |
+
* @param array $options Options to apply to the request
|
422 |
+
*
|
423 |
+
* @return RequestInterface
|
424 |
+
*/
|
425 |
+
protected function prepareRequest(RequestInterface $request, array $options = array())
|
426 |
+
{
|
427 |
+
$request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher());
|
428 |
+
|
429 |
+
if ($curl = $this->config[self::CURL_OPTIONS]) {
|
430 |
+
$request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl));
|
431 |
+
}
|
432 |
+
|
433 |
+
if ($params = $this->config[self::REQUEST_PARAMS]) {
|
434 |
+
Version::warn('request.params is deprecated. Use request.options to add default request options.');
|
435 |
+
$request->getParams()->overwriteWith($params);
|
436 |
+
}
|
437 |
+
|
438 |
+
if ($this->userAgent && !$request->hasHeader('User-Agent')) {
|
439 |
+
$request->setHeader('User-Agent', $this->userAgent);
|
440 |
+
}
|
441 |
+
|
442 |
+
if ($defaults = $this->config[self::REQUEST_OPTIONS]) {
|
443 |
+
$this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS);
|
444 |
+
}
|
445 |
+
|
446 |
+
if ($options) {
|
447 |
+
$this->requestFactory->applyOptions($request, $options);
|
448 |
+
}
|
449 |
+
|
450 |
+
$this->dispatch('client.create_request', array('client' => $this, 'request' => $request));
|
451 |
+
|
452 |
+
return $request;
|
453 |
+
}
|
454 |
+
|
455 |
+
/**
|
456 |
+
* Initializes SSL settings
|
457 |
+
*/
|
458 |
+
protected function initSsl()
|
459 |
+
{
|
460 |
+
if ('system' == ($authority = $this->config[self::SSL_CERT_AUTHORITY])) {
|
461 |
+
return;
|
462 |
+
}
|
463 |
+
|
464 |
+
if ($authority === null) {
|
465 |
+
$authority = true;
|
466 |
+
}
|
467 |
+
|
468 |
+
if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') {
|
469 |
+
$authority = $this->preparePharCacert();
|
470 |
+
$that = $this;
|
471 |
+
$this->getEventDispatcher()->addListener('request.before_send', function ($event) use ($authority, $that) {
|
472 |
+
if ($authority == $event['request']->getCurlOptions()->get(CURLOPT_CAINFO)) {
|
473 |
+
$that->preparePharCacert(false);
|
474 |
+
}
|
475 |
+
});
|
476 |
+
}
|
477 |
+
|
478 |
+
$this->setSslVerification($authority);
|
479 |
+
}
|
480 |
+
|
481 |
+
/**
|
482 |
+
* @deprecated
|
483 |
+
*/
|
484 |
+
public function getDefaultHeaders()
|
485 |
+
{
|
486 |
+
Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options');
|
487 |
+
return $this->defaultHeaders;
|
488 |
+
}
|
489 |
+
|
490 |
+
/**
|
491 |
+
* @deprecated
|
492 |
+
*/
|
493 |
+
public function setDefaultHeaders($headers)
|
494 |
+
{
|
495 |
+
Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options');
|
496 |
+
if ($headers instanceof Collection) {
|
497 |
+
$this->defaultHeaders = $headers;
|
498 |
+
} elseif (is_array($headers)) {
|
499 |
+
$this->defaultHeaders = new Collection($headers);
|
500 |
+
} else {
|
501 |
+
throw new InvalidArgumentException('Headers must be an array or Collection');
|
502 |
+
}
|
503 |
+
|
504 |
+
return $this;
|
505 |
+
}
|
506 |
+
}
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http;
|
4 |
+
|
5 |
+
use Guzzle\Common\HasDispatcherInterface;
|
6 |
+
use Guzzle\Common\Collection;
|
7 |
+
use Guzzle\Common\Exception\InvalidArgumentException;
|
8 |
+
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
|
9 |
+
use Guzzle\Http\Message\RequestInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Client interface for send HTTP requests
|
13 |
+
*/
|
14 |
+
interface ClientInterface extends HasDispatcherInterface
|
15 |
+
{
|
16 |
+
const CREATE_REQUEST = 'client.create_request';
|
17 |
+
|
18 |
+
/** @var string RFC 1123 HTTP-Date */
|
19 |
+
const HTTP_DATE = 'D, d M Y H:i:s \G\M\T';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Set the configuration object to use with the client
|
23 |
+
*
|
24 |
+
* @param array|Collection $config Parameters that define how the client behaves
|
25 |
+
*
|
26 |
+
* @return self
|
27 |
+
*/
|
28 |
+
public function setConfig($config);
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Get a configuration setting or all of the configuration settings. The Collection result of this method can be
|
32 |
+
* modified to change the configuration settings of a client.
|
33 |
+
*
|
34 |
+
* A client should honor the following special values:
|
35 |
+
*
|
36 |
+
* - request.options: Associative array of default RequestFactory options to apply to each request
|
37 |
+
* - request.params: Associative array of request parameters (data values) to apply to each request
|
38 |
+
* - curl.options: Associative array of cURL configuration settings to apply to each request
|
39 |
+
* - ssl.certificate_authority: Path a CAINFO, CAPATH, true to use strict defaults, or false to disable verification
|
40 |
+
* - redirect.disable: Set to true to disable redirects
|
41 |
+
*
|
42 |
+
* @param bool|string $key Configuration value to retrieve. Set to FALSE to retrieve all values of the client.
|
43 |
+
* The object return can be modified, and modifications will affect the client's config.
|
44 |
+
* @return mixed|Collection
|
45 |
+
* @see \Guzzle\Http\Message\RequestFactoryInterface::applyOptions for a full list of request.options options
|
46 |
+
*/
|
47 |
+
public function getConfig($key = false);
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Create and return a new {@see RequestInterface} configured for the client.
|
51 |
+
*
|
52 |
+
* Use an absolute path to override the base path of the client, or a relative path to append to the base path of
|
53 |
+
* the client. The URI can contain the query string as well. Use an array to provide a URI template and additional
|
54 |
+
* variables to use in the URI template expansion.
|
55 |
+
*
|
56 |
+
* @param string $method HTTP method. Defaults to GET
|
57 |
+
* @param string|array $uri Resource URI.
|
58 |
+
* @param array|Collection $headers HTTP headers
|
59 |
+
* @param string|resource|array|EntityBodyInterface $body Entity body of request (POST/PUT) or response (GET)
|
60 |
+
* @param array $options Array of options to apply to the request
|
61 |
+
*
|
62 |
+
* @return RequestInterface
|
63 |
+
* @throws InvalidArgumentException if a URI array is passed that does not contain exactly two elements: the URI
|
64 |
+
* followed by template variables
|
65 |
+
*/
|
66 |
+
public function createRequest(
|
67 |
+
$method = RequestInterface::GET,
|
68 |
+
$uri = null,
|
69 |
+
$headers = null,
|
70 |
+
$body = null,
|
71 |
+
array $options = array()
|
72 |
+
);
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Create a GET request for the client
|
76 |
+
*
|
77 |
+
* @param string|array $uri Resource URI
|
78 |
+
* @param array|Collection $headers HTTP headers
|
79 |
+
* @param array $options Options to apply to the request. For BC compatibility, you can also pass a
|
80 |
+
* string to tell Guzzle to download the body of the response to a particular
|
81 |
+
* location. Use the 'body' option instead for forward compatibility.
|
82 |
+
* @return RequestInterface
|
83 |
+
* @see Guzzle\Http\ClientInterface::createRequest()
|
84 |
+
*/
|
85 |
+
public function get($uri = null, $headers = null, $options = array());
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Create a HEAD request for the client
|
89 |
+
*
|
90 |
+
* @param string|array $uri Resource URI
|
91 |
+
* @param array|Collection $headers HTTP headers
|
92 |
+
* @param array $options Options to apply to the request
|
93 |
+
*
|
94 |
+
* @return RequestInterface
|
95 |
+
* @see Guzzle\Http\ClientInterface::createRequest()
|
96 |
+
*/
|
97 |
+
public function head($uri = null, $headers = null, array $options = array());
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Create a DELETE request for the client
|
101 |
+
*
|
102 |
+
* @param string|array $uri Resource URI
|
103 |
+
* @param array|Collection $headers HTTP headers
|
104 |
+
* @param string|resource|EntityBodyInterface $body Body to send in the request
|
105 |
+
* @param array $options Options to apply to the request
|
106 |
+
*
|
107 |
+
* @return EntityEnclosingRequestInterface
|
108 |
+
* @see Guzzle\Http\ClientInterface::createRequest()
|
109 |
+
*/
|
110 |
+
public function delete($uri = null, $headers = null, $body = null, array $options = array());
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Create a PUT request for the client
|
114 |
+
*
|
115 |
+
* @param string|array $uri Resource URI
|
116 |
+
* @param array|Collection $headers HTTP headers
|
117 |
+
* @param string|resource|EntityBodyInterface $body Body to send in the request
|
118 |
+
* @param array $options Options to apply to the request
|
119 |
+
*
|
120 |
+
* @return EntityEnclosingRequestInterface
|
121 |
+
* @see Guzzle\Http\ClientInterface::createRequest()
|
122 |
+
*/
|
123 |
+
public function put($uri = null, $headers = null, $body = null, array $options = array());
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Create a PATCH request for the client
|
127 |
+
*
|
128 |
+
* @param string|array $uri Resource URI
|
129 |
+
* @param array|Collection $headers HTTP headers
|
130 |
+
* @param string|resource|EntityBodyInterface $body Body to send in the request
|
131 |
+
* @param array $options Options to apply to the request
|
132 |
+
*
|
133 |
+
* @return EntityEnclosingRequestInterface
|
134 |
+
* @see Guzzle\Http\ClientInterface::createRequest()
|
135 |
+
*/
|
136 |
+
public function patch($uri = null, $headers = null, $body = null, array $options = array());
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Create a POST request for the client
|
140 |
+
*
|
141 |
+
* @param string|array $uri Resource URI
|
142 |
+
* @param array|Collection $headers HTTP headers
|
143 |
+
* @param array|Collection|string|EntityBodyInterface $postBody POST body. Can be a string, EntityBody, or
|
144 |
+
* associative array of POST fields to send in the body of the
|
145 |
+
* request. Prefix a value in the array with the @ symbol to
|
146 |
+
* reference a file.
|
147 |
+
* @param array $options Options to apply to the request
|
148 |
+
*
|
149 |
+
* @return EntityEnclosingRequestInterface
|
150 |
+
* @see Guzzle\Http\ClientInterface::createRequest()
|
151 |
+
*/
|
152 |
+
public function post($uri = null, $headers = null, $postBody = null, array $options = array());
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Create an OPTIONS request for the client
|
156 |
+
*
|
157 |
+
* @param string|array $uri Resource URI
|
158 |
+
* @param array $options Options to apply to the request
|
159 |
+
*
|
160 |
+
* @return RequestInterface
|
161 |
+
* @see Guzzle\Http\ClientInterface::createRequest()
|
162 |
+
*/
|
163 |
+
public function options($uri = null, array $options = array());
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Sends a single request or an array of requests in parallel
|
167 |
+
*
|
168 |
+
* @param array|RequestInterface $requests One or more RequestInterface objects to send
|
169 |
+
*
|
170 |
+
* @return \Guzzle\Http\Message\Response|array Returns a single Response or an array of Response objects
|
171 |
+
*/
|
172 |
+
public function send($requests);
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Get the client's base URL as either an expanded or raw URI template
|
176 |
+
*
|
177 |
+
* @param bool $expand Set to FALSE to get the raw base URL without URI template expansion
|
178 |
+
*
|
179 |
+
* @return string|null
|
180 |
+
*/
|
181 |
+
public function getBaseUrl($expand = true);
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Set the base URL of the client
|
185 |
+
*
|
186 |
+
* @param string $url The base service endpoint URL of the webservice
|
187 |
+
*
|
188 |
+
* @return self
|
189 |
+
*/
|
190 |
+
public function setBaseUrl($url);
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Set the User-Agent header to be used on all requests from the client
|
194 |
+
*
|
195 |
+
* @param string $userAgent User agent string
|
196 |
+
* @param bool $includeDefault Set to true to prepend the value to Guzzle's default user agent string
|
197 |
+
*
|
198 |
+
* @return self
|
199 |
+
*/
|
200 |
+
public function setUserAgent($userAgent, $includeDefault = false);
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Set SSL verification options.
|
204 |
+
*
|
205 |
+
* Setting $certificateAuthority to TRUE will result in the bundled cacert.pem being used to verify against the
|
206 |
+
* remote host.
|
207 |
+
*
|
208 |
+
* Alternate certificates to verify against can be specified with the $certificateAuthority option set to the full
|
209 |
+
* path to a certificate file, or the path to a directory containing certificates.
|
210 |
+
*
|
211 |
+
* Setting $certificateAuthority to FALSE will turn off peer verification, unset the bundled cacert.pem, and
|
212 |
+
* disable host verification. Please don't do this unless you really know what you're doing, and why you're doing
|
213 |
+
* it.
|
214 |
+
*
|
215 |
+
* @param string|bool $certificateAuthority bool, file path, or directory path
|
216 |
+
* @param bool $verifyPeer FALSE to stop from verifying the peer's certificate.
|
217 |
+
* @param int $verifyHost Set to 1 to check the existence of a common name in the SSL peer
|
218 |
+
* certificate. 2 to check the existence of a common name and also verify
|
219 |
+
* that it matches the hostname provided.
|
220 |
+
* @return self
|
221 |
+
*/
|
222 |
+
public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2);
|
223 |
+
}
|
@@ -0,0 +1,451 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Curl;
|
4 |
+
|
5 |
+
use Guzzle\Common\Exception\InvalidArgumentException;
|
6 |
+
use Guzzle\Common\Exception\RuntimeException;
|
7 |
+
use Guzzle\Common\Collection;
|
8 |
+
use Guzzle\Http\Message\EntityEnclosingRequest;
|
9 |
+
use Guzzle\Http\Message\RequestInterface;
|
10 |
+
use Guzzle\Parser\ParserRegistry;
|
11 |
+
use Guzzle\Http\Url;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Immutable wrapper for a cURL handle
|
15 |
+
*/
|
16 |
+
class CurlHandle
|
17 |
+
{
|
18 |
+
const BODY_AS_STRING = 'body_as_string';
|
19 |
+
const PROGRESS = 'progress';
|
20 |
+
const DEBUG = 'debug';
|
21 |
+
|
22 |
+
/** @var Collection Curl options */
|
23 |
+
protected $options;
|
24 |
+
|
25 |
+
/** @var resource Curl resource handle */
|
26 |
+
protected $handle;
|
27 |
+
|
28 |
+
/** @var int CURLE_* error */
|
29 |
+
protected $errorNo = CURLE_OK;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Factory method to create a new curl handle based on an HTTP request.
|
33 |
+
*
|
34 |
+
* There are some helpful options you can set to enable specific behavior:
|
35 |
+
* - debug: Set to true to enable cURL debug functionality to track the actual headers sent over the wire.
|
36 |
+
* - progress: Set to true to enable progress function callbacks.
|
37 |
+
*
|
38 |
+
* @param RequestInterface $request Request
|
39 |
+
*
|
40 |
+
* @return CurlHandle
|
41 |
+
* @throws RuntimeException
|
42 |
+
*/
|
43 |
+
public static function factory(RequestInterface $request)
|
44 |
+
{
|
45 |
+
$requestCurlOptions = $request->getCurlOptions();
|
46 |
+
$mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io'));
|
47 |
+
$tempContentLength = null;
|
48 |
+
$method = $request->getMethod();
|
49 |
+
$bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING);
|
50 |
+
|
51 |
+
// Array of default cURL options.
|
52 |
+
$curlOptions = array(
|
53 |
+
CURLOPT_URL => $request->getUrl(),
|
54 |
+
CURLOPT_CONNECTTIMEOUT => 150,
|
55 |
+
CURLOPT_RETURNTRANSFER => false,
|
56 |
+
CURLOPT_HEADER => false,
|
57 |
+
CURLOPT_PORT => $request->getPort(),
|
58 |
+
CURLOPT_HTTPHEADER => array(),
|
59 |
+
CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'),
|
60 |
+
CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'),
|
61 |
+
CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0'
|
62 |
+
? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1,
|
63 |
+
// Verifies the authenticity of the peer's certificate
|
64 |
+
CURLOPT_SSL_VERIFYPEER => 1,
|
65 |
+
// Certificate must indicate that the server is the server to which you meant to connect
|
66 |
+
CURLOPT_SSL_VERIFYHOST => 2
|
67 |
+
);
|
68 |
+
|
69 |
+
if (defined('CURLOPT_PROTOCOLS')) {
|
70 |
+
// Allow only HTTP and HTTPS protocols
|
71 |
+
$curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
72 |
+
}
|
73 |
+
|
74 |
+
// Add CURLOPT_ENCODING if Accept-Encoding header is provided
|
75 |
+
if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) {
|
76 |
+
$curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader;
|
77 |
+
// Let cURL set the Accept-Encoding header, prevents duplicate values
|
78 |
+
$request->removeHeader('Accept-Encoding');
|
79 |
+
}
|
80 |
+
|
81 |
+
// Enable curl debug information if the 'debug' param was set
|
82 |
+
if ($requestCurlOptions->get('debug')) {
|
83 |
+
$curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+');
|
84 |
+
// @codeCoverageIgnoreStart
|
85 |
+
if (false === $curlOptions[CURLOPT_STDERR]) {
|
86 |
+
throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR');
|
87 |
+
}
|
88 |
+
// @codeCoverageIgnoreEnd
|
89 |
+
$curlOptions[CURLOPT_VERBOSE] = true;
|
90 |
+
}
|
91 |
+
|
92 |
+
// Specify settings according to the HTTP method
|
93 |
+
if ($method == 'GET') {
|
94 |
+
$curlOptions[CURLOPT_HTTPGET] = true;
|
95 |
+
} elseif ($method == 'HEAD') {
|
96 |
+
$curlOptions[CURLOPT_NOBODY] = true;
|
97 |
+
// HEAD requests do not use a write function
|
98 |
+
unset($curlOptions[CURLOPT_WRITEFUNCTION]);
|
99 |
+
} elseif (!($request instanceof EntityEnclosingRequest)) {
|
100 |
+
$curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
|
101 |
+
} else {
|
102 |
+
|
103 |
+
$curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
|
104 |
+
|
105 |
+
// Handle sending raw bodies in a request
|
106 |
+
if ($request->getBody()) {
|
107 |
+
// You can send the body as a string using curl's CURLOPT_POSTFIELDS
|
108 |
+
if ($bodyAsString) {
|
109 |
+
$curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody();
|
110 |
+
// Allow curl to add the Content-Length for us to account for the times when
|
111 |
+
// POST redirects are followed by GET requests
|
112 |
+
if ($tempContentLength = $request->getHeader('Content-Length')) {
|
113 |
+
$tempContentLength = (int) (string) $tempContentLength;
|
114 |
+
}
|
115 |
+
// Remove the curl generated Content-Type header if none was set manually
|
116 |
+
if (!$request->hasHeader('Content-Type')) {
|
117 |
+
$curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:';
|
118 |
+
}
|
119 |
+
} else {
|
120 |
+
$curlOptions[CURLOPT_UPLOAD] = true;
|
121 |
+
// Let cURL handle setting the Content-Length header
|
122 |
+
if ($tempContentLength = $request->getHeader('Content-Length')) {
|
123 |
+
$tempContentLength = (int) (string) $tempContentLength;
|
124 |
+
$curlOptions[CURLOPT_INFILESIZE] = $tempContentLength;
|
125 |
+
}
|
126 |
+
// Add a callback for curl to read data to send with the request only if a body was specified
|
127 |
+
$curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody');
|
128 |
+
// Attempt to seek to the start of the stream
|
129 |
+
$request->getBody()->seek(0);
|
130 |
+
}
|
131 |
+
|
132 |
+
} else {
|
133 |
+
|
134 |
+
// Special handling for POST specific fields and files
|
135 |
+
$postFields = false;
|
136 |
+
if (count($request->getPostFiles())) {
|
137 |
+
$postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode();
|
138 |
+
foreach ($request->getPostFiles() as $key => $data) {
|
139 |
+
$prefixKeys = count($data) > 1;
|
140 |
+
foreach ($data as $index => $file) {
|
141 |
+
// Allow multiple files in the same key
|
142 |
+
$fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key;
|
143 |
+
$postFields[$fieldKey] = $file->getCurlValue();
|
144 |
+
}
|
145 |
+
}
|
146 |
+
} elseif (count($request->getPostFields())) {
|
147 |
+
$postFields = (string) $request->getPostFields()->useUrlEncoding(true);
|
148 |
+
}
|
149 |
+
|
150 |
+
if ($postFields !== false) {
|
151 |
+
if ($method == 'POST') {
|
152 |
+
unset($curlOptions[CURLOPT_CUSTOMREQUEST]);
|
153 |
+
$curlOptions[CURLOPT_POST] = true;
|
154 |
+
}
|
155 |
+
$curlOptions[CURLOPT_POSTFIELDS] = $postFields;
|
156 |
+
$request->removeHeader('Content-Length');
|
157 |
+
}
|
158 |
+
}
|
159 |
+
|
160 |
+
// If the Expect header is not present, prevent curl from adding it
|
161 |
+
if (!$request->hasHeader('Expect')) {
|
162 |
+
$curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:';
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
// If a Content-Length header was specified but we want to allow curl to set one for us
|
167 |
+
if (null !== $tempContentLength) {
|
168 |
+
$request->removeHeader('Content-Length');
|
169 |
+
}
|
170 |
+
|
171 |
+
// Set custom cURL options
|
172 |
+
foreach ($requestCurlOptions->toArray() as $key => $value) {
|
173 |
+
if (is_numeric($key)) {
|
174 |
+
$curlOptions[$key] = $value;
|
175 |
+
}
|
176 |
+
}
|
177 |
+
|
178 |
+
// Do not set an Accept header by default
|
179 |
+
if (!isset($curlOptions[CURLOPT_ENCODING])) {
|
180 |
+
$curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:';
|
181 |
+
}
|
182 |
+
|
183 |
+
// Add any custom headers to the request. Empty headers will cause curl to not send the header at all.
|
184 |
+
foreach ($request->getHeaderLines() as $line) {
|
185 |
+
$curlOptions[CURLOPT_HTTPHEADER][] = $line;
|
186 |
+
}
|
187 |
+
|
188 |
+
// Add the content-length header back if it was temporarily removed
|
189 |
+
if ($tempContentLength) {
|
190 |
+
$request->setHeader('Content-Length', $tempContentLength);
|
191 |
+
}
|
192 |
+
|
193 |
+
// Apply the options to a new cURL handle.
|
194 |
+
$handle = curl_init();
|
195 |
+
|
196 |
+
// Enable the progress function if the 'progress' param was set
|
197 |
+
if ($requestCurlOptions->get('progress')) {
|
198 |
+
// Wrap the function in a function that provides the curl handle to the mediator's progress function
|
199 |
+
// Using this rather than injecting the handle into the mediator prevents a circular reference
|
200 |
+
$curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) {
|
201 |
+
$args = func_get_args();
|
202 |
+
$args[] = $handle;
|
203 |
+
call_user_func_array(array($mediator, 'progress'), $args);
|
204 |
+
};
|
205 |
+
$curlOptions[CURLOPT_NOPROGRESS] = false;
|
206 |
+
}
|
207 |
+
|
208 |
+
curl_setopt_array($handle, $curlOptions);
|
209 |
+
|
210 |
+
return new static($handle, $curlOptions);
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Construct a new CurlHandle object that wraps a cURL handle
|
215 |
+
*
|
216 |
+
* @param resource $handle Configured cURL handle resource
|
217 |
+
* @param Collection|array $options Curl options to use with the handle
|
218 |
+
*
|
219 |
+
* @throws InvalidArgumentException
|
220 |
+
*/
|
221 |
+
public function __construct($handle, $options)
|
222 |
+
{
|
223 |
+
if (!is_resource($handle)) {
|
224 |
+
throw new InvalidArgumentException('Invalid handle provided');
|
225 |
+
}
|
226 |
+
if (is_array($options)) {
|
227 |
+
$this->options = new Collection($options);
|
228 |
+
} elseif ($options instanceof Collection) {
|
229 |
+
$this->options = $options;
|
230 |
+
} else {
|
231 |
+
throw new InvalidArgumentException('Expected array or Collection');
|
232 |
+
}
|
233 |
+
$this->handle = $handle;
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Destructor
|
238 |
+
*/
|
239 |
+
public function __destruct()
|
240 |
+
{
|
241 |
+
$this->close();
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Close the curl handle
|
246 |
+
*/
|
247 |
+
public function close()
|
248 |
+
{
|
249 |
+
if (is_resource($this->handle)) {
|
250 |
+
curl_close($this->handle);
|
251 |
+
}
|
252 |
+
$this->handle = null;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Check if the handle is available and still OK
|
257 |
+
*
|
258 |
+
* @return bool
|
259 |
+
*/
|
260 |
+
public function isAvailable()
|
261 |
+
{
|
262 |
+
return is_resource($this->handle);
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Get the last error that occurred on the cURL handle
|
267 |
+
*
|
268 |
+
* @return string
|
269 |
+
*/
|
270 |
+
public function getError()
|
271 |
+
{
|
272 |
+
return $this->isAvailable() ? curl_error($this->handle) : '';
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Get the last error number that occurred on the cURL handle
|
277 |
+
*
|
278 |
+
* @return int
|
279 |
+
*/
|
280 |
+
public function getErrorNo()
|
281 |
+
{
|
282 |
+
if ($this->errorNo) {
|
283 |
+
return $this->errorNo;
|
284 |
+
}
|
285 |
+
|
286 |
+
return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK;
|
287 |
+
}
|
288 |
+
|
289 |
+
/**
|
290 |
+
* Set the curl error number
|
291 |
+
*
|
292 |
+
* @param int $error Error number to set
|
293 |
+
*
|
294 |
+
* @return CurlHandle
|
295 |
+
*/
|
296 |
+
public function setErrorNo($error)
|
297 |
+
{
|
298 |
+
$this->errorNo = $error;
|
299 |
+
|
300 |
+
return $this;
|
301 |
+
}
|
302 |
+
|
303 |
+
/**
|
304 |
+
* Get cURL curl_getinfo data
|
305 |
+
*
|
306 |
+
* @param int $option Option to retrieve. Pass null to retrieve all data as an array.
|
307 |
+
*
|
308 |
+
* @return array|mixed
|
309 |
+
*/
|
310 |
+
public function getInfo($option = null)
|
311 |
+
{
|
312 |
+
if (!is_resource($this->handle)) {
|
313 |
+
return null;
|
314 |
+
}
|
315 |
+
|
316 |
+
if (null !== $option) {
|
317 |
+
return curl_getinfo($this->handle, $option) ?: null;
|
318 |
+
}
|
319 |
+
|
320 |
+
return curl_getinfo($this->handle) ?: array();
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Get the stderr output
|
325 |
+
*
|
326 |
+
* @param bool $asResource Set to TRUE to get an fopen resource
|
327 |
+
*
|
328 |
+
* @return string|resource|null
|
329 |
+
*/
|
330 |
+
public function getStderr($asResource = false)
|
331 |
+
{
|
332 |
+
$stderr = $this->getOptions()->get(CURLOPT_STDERR);
|
333 |
+
if (!$stderr) {
|
334 |
+
return null;
|
335 |
+
}
|
336 |
+
|
337 |
+
if ($asResource) {
|
338 |
+
return $stderr;
|
339 |
+
}
|
340 |
+
|
341 |
+
fseek($stderr, 0);
|
342 |
+
$e = stream_get_contents($stderr);
|
343 |
+
fseek($stderr, 0, SEEK_END);
|
344 |
+
|
345 |
+
return $e;
|
346 |
+
}
|
347 |
+
|
348 |
+
/**
|
349 |
+
* Get the URL that this handle is connecting to
|
350 |
+
*
|
351 |
+
* @return Url
|
352 |
+
*/
|
353 |
+
public function getUrl()
|
354 |
+
{
|
355 |
+
return Url::factory($this->options->get(CURLOPT_URL));
|
356 |
+
}
|
357 |
+
|
358 |
+
/**
|
359 |
+
* Get the wrapped curl handle
|
360 |
+
*
|
361 |
+
* @return resource|null Returns the cURL handle or null if it was closed
|
362 |
+
*/
|
363 |
+
public function getHandle()
|
364 |
+
{
|
365 |
+
return $this->isAvailable() ? $this->handle : null;
|
366 |
+
}
|
367 |
+
|
368 |
+
/**
|
369 |
+
* Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl
|
370 |
+
* handle after it is created.
|
371 |
+
*
|
372 |
+
* @return Collection
|
373 |
+
*/
|
374 |
+
public function getOptions()
|
375 |
+
{
|
376 |
+
return $this->options;
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Update a request based on the log messages of the CurlHandle
|
381 |
+
*
|
382 |
+
* @param RequestInterface $request Request to update
|
383 |
+
*/
|
384 |
+
public function updateRequestFromTransfer(RequestInterface $request)
|
385 |
+
{
|
386 |
+
if (!$request->getResponse()) {
|
387 |
+
return;
|
388 |
+
}
|
389 |
+
|
390 |
+
// Update the transfer stats of the response
|
391 |
+
$request->getResponse()->setInfo($this->getInfo());
|
392 |
+
|
393 |
+
if (!$log = $this->getStderr(true)) {
|
394 |
+
return;
|
395 |
+
}
|
396 |
+
|
397 |
+
// Parse the cURL stderr output for outgoing requests
|
398 |
+
$headers = '';
|
399 |
+
fseek($log, 0);
|
400 |
+
while (($line = fgets($log)) !== false) {
|
401 |
+
if ($line && $line[0] == '>') {
|
402 |
+
$headers = substr(trim($line), 2) . "\r\n";
|
403 |
+
while (($line = fgets($log)) !== false) {
|
404 |
+
if ($line[0] == '*' || $line[0] == '<') {
|
405 |
+
break;
|
406 |
+
} else {
|
407 |
+
$headers .= trim($line) . "\r\n";
|
408 |
+
}
|
409 |
+
}
|
410 |
+
}
|
411 |
+
}
|
412 |
+
|
413 |
+
// Add request headers to the request exactly as they were sent
|
414 |
+
if ($headers) {
|
415 |
+
$parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers);
|
416 |
+
if (!empty($parsed['headers'])) {
|
417 |
+
$request->setHeaders(array());
|
418 |
+
foreach ($parsed['headers'] as $name => $value) {
|
419 |
+
$request->setHeader($name, $value);
|
420 |
+
}
|
421 |
+
}
|
422 |
+
if (!empty($parsed['version'])) {
|
423 |
+
$request->setProtocolVersion($parsed['version']);
|
424 |
+
}
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere
|
430 |
+
*
|
431 |
+
* @param array|Collection $config The configuration we want to parse
|
432 |
+
*
|
433 |
+
* @return array
|
434 |
+
*/
|
435 |
+
public static function parseCurlConfig($config)
|
436 |
+
{
|
437 |
+
$curlOptions = array();
|
438 |
+
foreach ($config as $key => $value) {
|
439 |
+
if (is_string($key) && defined($key)) {
|
440 |
+
// Convert constants represented as string to constant int values
|
441 |
+
$key = constant($key);
|
442 |
+
}
|
443 |
+
if (is_string($value) && defined($value)) {
|
444 |
+
$value = constant($value);
|
445 |
+
}
|
446 |
+
$curlOptions[$key] = $value;
|
447 |
+
}
|
448 |
+
|
449 |
+
return $curlOptions;
|
450 |
+
}
|
451 |
+
}
|
@@ -0,0 +1,363 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Curl;
|
4 |
+
|
5 |
+
use Guzzle\Common\AbstractHasDispatcher;
|
6 |
+
use Guzzle\Common\Event;
|
7 |
+
use Guzzle\Http\Exception\MultiTransferException;
|
8 |
+
use Guzzle\Http\Exception\CurlException;
|
9 |
+
use Guzzle\Http\Message\RequestInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Send {@see RequestInterface} objects in parallel using curl_multi
|
13 |
+
*/
|
14 |
+
class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
|
15 |
+
{
|
16 |
+
/** @var resource cURL multi handle. */
|
17 |
+
protected $multiHandle;
|
18 |
+
|
19 |
+
/** @var array Attached {@see RequestInterface} objects. */
|
20 |
+
protected $requests;
|
21 |
+
|
22 |
+
/** @var \SplObjectStorage RequestInterface to CurlHandle hash */
|
23 |
+
protected $handles;
|
24 |
+
|
25 |
+
/** @var array Hash mapping curl handle resource IDs to request objects */
|
26 |
+
protected $resourceHash;
|
27 |
+
|
28 |
+
/** @var array Queued exceptions */
|
29 |
+
protected $exceptions = array();
|
30 |
+
|
31 |
+
/** @var array Requests that succeeded */
|
32 |
+
protected $successful = array();
|
33 |
+
|
34 |
+
/** @var array cURL multi error values and codes */
|
35 |
+
protected $multiErrors = array(
|
36 |
+
CURLM_BAD_HANDLE => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'),
|
37 |
+
CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."),
|
38 |
+
CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'),
|
39 |
+
CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!')
|
40 |
+
);
|
41 |
+
|
42 |
+
public function __construct()
|
43 |
+
{
|
44 |
+
$this->multiHandle = curl_multi_init();
|
45 |
+
// @codeCoverageIgnoreStart
|
46 |
+
if ($this->multiHandle === false) {
|
47 |
+
throw new CurlException('Unable to create multi handle');
|
48 |
+
}
|
49 |
+
// @codeCoverageIgnoreEnd
|
50 |
+
$this->reset();
|
51 |
+
}
|
52 |
+
|
53 |
+
public function __destruct()
|
54 |
+
{
|
55 |
+
if (is_resource($this->multiHandle)) {
|
56 |
+
curl_multi_close($this->multiHandle);
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
public function add(RequestInterface $request)
|
61 |
+
{
|
62 |
+
$this->requests[] = $request;
|
63 |
+
// If requests are currently transferring and this is async, then the
|
64 |
+
// request must be prepared now as the send() method is not called.
|
65 |
+
$this->beforeSend($request);
|
66 |
+
$this->dispatch(self::ADD_REQUEST, array('request' => $request));
|
67 |
+
|
68 |
+
return $this;
|
69 |
+
}
|
70 |
+
|
71 |
+
public function all()
|
72 |
+
{
|
73 |
+
return $this->requests;
|
74 |
+
}
|
75 |
+
|
76 |
+
public function remove(RequestInterface $request)
|
77 |
+
{
|
78 |
+
$this->removeHandle($request);
|
79 |
+
if (($index = array_search($request, $this->requests, true)) !== false) {
|
80 |
+
$request = $this->requests[$index];
|
81 |
+
unset($this->requests[$index]);
|
82 |
+
$this->requests = array_values($this->requests);
|
83 |
+
$this->dispatch(self::REMOVE_REQUEST, array('request' => $request));
|
84 |
+
return true;
|
85 |
+
}
|
86 |
+
|
87 |
+
return false;
|
88 |
+
}
|
89 |
+
|
90 |
+
public function reset($hard = false)
|
91 |
+
{
|
92 |
+
// Remove each request
|
93 |
+
if ($this->requests) {
|
94 |
+
foreach ($this->requests as $request) {
|
95 |
+
$this->remove($request);
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
$this->handles = new \SplObjectStorage();
|
100 |
+
$this->requests = $this->resourceHash = $this->exceptions = $this->successful = array();
|
101 |
+
}
|
102 |
+
|
103 |
+
public function send()
|
104 |
+
{
|
105 |
+
$this->perform();
|
106 |
+
$exceptions = $this->exceptions;
|
107 |
+
$successful = $this->successful;
|
108 |
+
$this->reset();
|
109 |
+
|
110 |
+
if ($exceptions) {
|
111 |
+
$this->throwMultiException($exceptions, $successful);
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
public function count()
|
116 |
+
{
|
117 |
+
return count($this->requests);
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Build and throw a MultiTransferException
|
122 |
+
*
|
123 |
+
* @param array $exceptions Exceptions encountered
|
124 |
+
* @param array $successful Successful requests
|
125 |
+
* @throws MultiTransferException
|
126 |
+
*/
|
127 |
+
protected function throwMultiException(array $exceptions, array $successful)
|
128 |
+
{
|
129 |
+
$multiException = new MultiTransferException('Errors during multi transfer');
|
130 |
+
|
131 |
+
while ($e = array_shift($exceptions)) {
|
132 |
+
$multiException->addFailedRequestWithException($e['request'], $e['exception']);
|
133 |
+
}
|
134 |
+
|
135 |
+
// Add successful requests
|
136 |
+
foreach ($successful as $request) {
|
137 |
+
if (!$multiException->containsRequest($request)) {
|
138 |
+
$multiException->addSuccessfulRequest($request);
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
throw $multiException;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Prepare for sending
|
147 |
+
*
|
148 |
+
* @param RequestInterface $request Request to prepare
|
149 |
+
* @throws \Exception on error preparing the request
|
150 |
+
*/
|
151 |
+
protected function beforeSend(RequestInterface $request)
|
152 |
+
{
|
153 |
+
try {
|
154 |
+
$state = $request->setState(RequestInterface::STATE_TRANSFER);
|
155 |
+
if ($state == RequestInterface::STATE_TRANSFER) {
|
156 |
+
// Add the request curl handle to the multi handle
|
157 |
+
$handle = $this->createCurlHandle($request)->getHandle();
|
158 |
+
$this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $handle));
|
159 |
+
} else {
|
160 |
+
// Requests might decide they don't need to be sent just before transfer (e.g. CachePlugin)
|
161 |
+
$this->remove($request);
|
162 |
+
if ($state == RequestInterface::STATE_COMPLETE) {
|
163 |
+
$this->successful[] = $request;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
} catch (\Exception $e) {
|
167 |
+
// Queue the exception to be thrown when sent
|
168 |
+
$this->removeErroredRequest($request, $e);
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Create a curl handle for a request
|
174 |
+
*
|
175 |
+
* @param RequestInterface $request Request
|
176 |
+
*
|
177 |
+
* @return CurlHandle
|
178 |
+
*/
|
179 |
+
protected function createCurlHandle(RequestInterface $request)
|
180 |
+
{
|
181 |
+
$wrapper = CurlHandle::factory($request);
|
182 |
+
$this->handles[$request] = $wrapper;
|
183 |
+
$this->resourceHash[(int) $wrapper->getHandle()] = $request;
|
184 |
+
|
185 |
+
return $wrapper;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Get the data from the multi handle
|
190 |
+
*/
|
191 |
+
protected function perform()
|
192 |
+
{
|
193 |
+
$event = new Event(array('curl_multi' => $this));
|
194 |
+
|
195 |
+
while ($this->requests) {
|
196 |
+
// Notify each request as polling
|
197 |
+
$blocking = $total = 0;
|
198 |
+
foreach ($this->requests as $request) {
|
199 |
+
++$total;
|
200 |
+
$event['request'] = $request;
|
201 |
+
$request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event);
|
202 |
+
// The blocking variable just has to be non-falsey to block the loop
|
203 |
+
if ($request->getParams()->hasKey(self::BLOCKING)) {
|
204 |
+
++$blocking;
|
205 |
+
}
|
206 |
+
}
|
207 |
+
if ($blocking == $total) {
|
208 |
+
// Sleep to prevent eating CPU because no requests are actually pending a select call
|
209 |
+
usleep(500);
|
210 |
+
} else {
|
211 |
+
$this->executeHandles();
|
212 |
+
}
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Execute and select curl handles
|
218 |
+
*/
|
219 |
+
private function executeHandles()
|
220 |
+
{
|
221 |
+
// The first curl_multi_select often times out no matter what, but is usually required for fast transfers
|
222 |
+
$selectTimeout = 0.001;
|
223 |
+
$active = false;
|
224 |
+
do {
|
225 |
+
while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM);
|
226 |
+
$this->checkCurlResult($mrc);
|
227 |
+
$this->processMessages();
|
228 |
+
if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) {
|
229 |
+
// Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141
|
230 |
+
usleep(150);
|
231 |
+
}
|
232 |
+
$selectTimeout = 1;
|
233 |
+
} while ($active);
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Process any received curl multi messages
|
238 |
+
*/
|
239 |
+
private function processMessages()
|
240 |
+
{
|
241 |
+
while ($done = curl_multi_info_read($this->multiHandle)) {
|
242 |
+
$request = $this->resourceHash[(int) $done['handle']];
|
243 |
+
try {
|
244 |
+
$this->processResponse($request, $this->handles[$request], $done);
|
245 |
+
$this->successful[] = $request;
|
246 |
+
} catch (\Exception $e) {
|
247 |
+
$this->removeErroredRequest($request, $e);
|
248 |
+
}
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Remove a request that encountered an exception
|
254 |
+
*
|
255 |
+
* @param RequestInterface $request Request to remove
|
256 |
+
* @param \Exception $e Exception encountered
|
257 |
+
*/
|
258 |
+
protected function removeErroredRequest(RequestInterface $request, \Exception $e = null)
|
259 |
+
{
|
260 |
+
$this->exceptions[] = array('request' => $request, 'exception' => $e);
|
261 |
+
$this->remove($request);
|
262 |
+
$this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions));
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Check for errors and fix headers of a request based on a curl response
|
267 |
+
*
|
268 |
+
* @param RequestInterface $request Request to process
|
269 |
+
* @param CurlHandle $handle Curl handle object
|
270 |
+
* @param array $curl Array returned from curl_multi_info_read
|
271 |
+
*
|
272 |
+
* @throws CurlException on Curl error
|
273 |
+
*/
|
274 |
+
protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl)
|
275 |
+
{
|
276 |
+
// Set the transfer stats on the response
|
277 |
+
$handle->updateRequestFromTransfer($request);
|
278 |
+
// Check if a cURL exception occurred, and if so, notify things
|
279 |
+
$curlException = $this->isCurlException($request, $handle, $curl);
|
280 |
+
|
281 |
+
// Always remove completed curl handles. They can be added back again
|
282 |
+
// via events if needed (e.g. ExponentialBackoffPlugin)
|
283 |
+
$this->removeHandle($request);
|
284 |
+
|
285 |
+
if (!$curlException) {
|
286 |
+
$state = $request->setState(RequestInterface::STATE_COMPLETE, array('handle' => $handle));
|
287 |
+
// Only remove the request if it wasn't resent as a result of the state change
|
288 |
+
if ($state != RequestInterface::STATE_TRANSFER) {
|
289 |
+
$this->remove($request);
|
290 |
+
}
|
291 |
+
} else {
|
292 |
+
// Set the state of the request to an error
|
293 |
+
$state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException));
|
294 |
+
// Allow things to ignore the error if possible
|
295 |
+
if ($state != RequestInterface::STATE_TRANSFER) {
|
296 |
+
$this->remove($request);
|
297 |
+
}
|
298 |
+
// The error was not handled, so fail
|
299 |
+
if ($state == RequestInterface::STATE_ERROR) {
|
300 |
+
/** @var CurlException $curlException */
|
301 |
+
throw $curlException;
|
302 |
+
}
|
303 |
+
}
|
304 |
+
}
|
305 |
+
|
306 |
+
/**
|
307 |
+
* Remove a curl handle from the curl multi object
|
308 |
+
*
|
309 |
+
* @param RequestInterface $request Request that owns the handle
|
310 |
+
*/
|
311 |
+
protected function removeHandle(RequestInterface $request)
|
312 |
+
{
|
313 |
+
if (isset($this->handles[$request])) {
|
314 |
+
$handle = $this->handles[$request];
|
315 |
+
curl_multi_remove_handle($this->multiHandle, $handle->getHandle());
|
316 |
+
unset($this->handles[$request]);
|
317 |
+
unset($this->resourceHash[(int) $handle->getHandle()]);
|
318 |
+
$handle->close();
|
319 |
+
}
|
320 |
+
}
|
321 |
+
|
322 |
+
/**
|
323 |
+
* Check if a cURL transfer resulted in what should be an exception
|
324 |
+
*
|
325 |
+
* @param RequestInterface $request Request to check
|
326 |
+
* @param CurlHandle $handle Curl handle object
|
327 |
+
* @param array $curl Array returned from curl_multi_info_read
|
328 |
+
*
|
329 |
+
* @return CurlException|bool
|
330 |
+
*/
|
331 |
+
private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl)
|
332 |
+
{
|
333 |
+
if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) {
|
334 |
+
return false;
|
335 |
+
}
|
336 |
+
|
337 |
+
$handle->setErrorNo($curl['result']);
|
338 |
+
$e = new CurlException(sprintf('[curl] %s: %s [url] %s',
|
339 |
+
$handle->getErrorNo(), $handle->getError(), $handle->getUrl()));
|
340 |
+
$e->setCurlHandle($handle)
|
341 |
+
->setRequest($request)
|
342 |
+
->setCurlInfo($handle->getInfo())
|
343 |
+
->setError($handle->getError(), $handle->getErrorNo());
|
344 |
+
|
345 |
+
return $e;
|
346 |
+
}
|
347 |
+
|
348 |
+
/**
|
349 |
+
* Throw an exception for a cURL multi response if needed
|
350 |
+
*
|
351 |
+
* @param int $code Curl response code
|
352 |
+
* @throws CurlException
|
353 |
+
*/
|
354 |
+
private function checkCurlResult($code)
|
355 |
+
{
|
356 |
+
if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) {
|
357 |
+
throw new CurlException(isset($this->multiErrors[$code])
|
358 |
+
? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}"
|
359 |
+
: 'Unexpected cURL error: ' . $code
|
360 |
+
);
|
361 |
+
}
|
362 |
+
}
|
363 |
+
}
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Curl;
|
4 |
+
|
5 |
+
use Guzzle\Common\HasDispatcherInterface;
|
6 |
+
use Guzzle\Common\Exception\ExceptionCollection;
|
7 |
+
use Guzzle\Http\Message\RequestInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Interface for sending a pool of {@see RequestInterface} objects in parallel
|
11 |
+
*/
|
12 |
+
interface CurlMultiInterface extends \Countable, HasDispatcherInterface
|
13 |
+
{
|
14 |
+
const POLLING_REQUEST = 'curl_multi.polling_request';
|
15 |
+
const ADD_REQUEST = 'curl_multi.add_request';
|
16 |
+
const REMOVE_REQUEST = 'curl_multi.remove_request';
|
17 |
+
const MULTI_EXCEPTION = 'curl_multi.exception';
|
18 |
+
const BLOCKING = 'curl_multi.blocking';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Add a request to the pool.
|
22 |
+
*
|
23 |
+
* @param RequestInterface $request Request to add
|
24 |
+
*
|
25 |
+
* @return CurlMultiInterface
|
26 |
+
*/
|
27 |
+
public function add(RequestInterface $request);
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Get an array of attached {@see RequestInterface} objects
|
31 |
+
*
|
32 |
+
* @return array
|
33 |
+
*/
|
34 |
+
public function all();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Remove a request from the pool.
|
38 |
+
*
|
39 |
+
* @param RequestInterface $request Request to remove
|
40 |
+
*
|
41 |
+
* @return bool Returns true on success or false on failure
|
42 |
+
*/
|
43 |
+
public function remove(RequestInterface $request);
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Reset the state and remove any attached RequestInterface objects
|
47 |
+
*
|
48 |
+
* @param bool $hard Set to true to close and reopen any open multi handles
|
49 |
+
*/
|
50 |
+
public function reset($hard = false);
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Send a pool of {@see RequestInterface} requests.
|
54 |
+
*
|
55 |
+
* @throws ExceptionCollection if any requests threw exceptions during the transfer.
|
56 |
+
*/
|
57 |
+
public function send();
|
58 |
+
}
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Curl;
|
4 |
+
|
5 |
+
use Guzzle\Common\AbstractHasDispatcher;
|
6 |
+
use Guzzle\Http\Message\RequestInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Proxies requests and connections to a pool of internal curl_multi handles. Each recursive call will add requests
|
10 |
+
* to the next available CurlMulti handle.
|
11 |
+
*/
|
12 |
+
class CurlMultiProxy extends AbstractHasDispatcher implements CurlMultiInterface
|
13 |
+
{
|
14 |
+
protected $handles = array();
|
15 |
+
protected $groups = array();
|
16 |
+
protected $queued = array();
|
17 |
+
protected $maxHandles;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @param int $maxHandles The maximum number of idle CurlMulti handles to allow to remain open
|
21 |
+
*/
|
22 |
+
public function __construct($maxHandles = 3)
|
23 |
+
{
|
24 |
+
$this->maxHandles = $maxHandles;
|
25 |
+
// You can get some weird "Too many open files" errors when sending a large amount of requests in parallel.
|
26 |
+
// These two statements autoload classes before a system runs out of file descriptors so that you can get back
|
27 |
+
// valuable error messages if you run out.
|
28 |
+
class_exists('Guzzle\Http\Message\Response');
|
29 |
+
class_exists('Guzzle\Http\Exception\CurlException');
|
30 |
+
}
|
31 |
+
|
32 |
+
public function add(RequestInterface $request)
|
33 |
+
{
|
34 |
+
$this->queued[] = $request;
|
35 |
+
|
36 |
+
return $this;
|
37 |
+
}
|
38 |
+
|
39 |
+
public function all()
|
40 |
+
{
|
41 |
+
$requests = $this->queued;
|
42 |
+
foreach ($this->handles as $handle) {
|
43 |
+
$requests = array_merge($requests, $handle->all());
|
44 |
+
}
|
45 |
+
|
46 |
+
return $requests;
|
47 |
+
}
|
48 |
+
|
49 |
+
public function remove(RequestInterface $request)
|
50 |
+
{
|
51 |
+
foreach ($this->queued as $i => $r) {
|
52 |
+
if ($request === $r) {
|
53 |
+
unset($this->queued[$i]);
|
54 |
+
return true;
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
foreach ($this->handles as $handle) {
|
59 |
+
if ($handle->remove($request)) {
|
60 |
+
return true;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
return false;
|
65 |
+
}
|
66 |
+
|
67 |
+
public function reset($hard = false)
|
68 |
+
{
|
69 |
+
$this->queued = array();
|
70 |
+
$this->groups = array();
|
71 |
+
foreach ($this->handles as $handle) {
|
72 |
+
$handle->reset();
|
73 |
+
}
|
74 |
+
if ($hard) {
|
75 |
+
$this->handles = array();
|
76 |
+
}
|
77 |
+
|
78 |
+
return $this;
|
79 |
+
}
|
80 |
+
|
81 |
+
public function send()
|
82 |
+
{
|
83 |
+
if ($this->queued) {
|
84 |
+
$group = $this->getAvailableHandle();
|
85 |
+
// Add this handle to a list of handles than is claimed
|
86 |
+
$this->groups[] = $group;
|
87 |
+
while ($request = array_shift($this->queued)) {
|
88 |
+
$group->add($request);
|
89 |
+
}
|
90 |
+
try {
|
91 |
+
$group->send();
|
92 |
+
array_pop($this->groups);
|
93 |
+
$this->cleanupHandles();
|
94 |
+
} catch (\Exception $e) {
|
95 |
+
// Remove the group and cleanup if an exception was encountered and no more requests in group
|
96 |
+
if (!$group->count()) {
|
97 |
+
array_pop($this->groups);
|
98 |
+
$this->cleanupHandles();
|
99 |
+
}
|
100 |
+
throw $e;
|
101 |
+
}
|
102 |
+
}
|
103 |
+
}
|
104 |
+
|
105 |
+
public function count()
|
106 |
+
{
|
107 |
+
return count($this->all());
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Get an existing available CurlMulti handle or create a new one
|
112 |
+
*
|
113 |
+
* @return CurlMulti
|
114 |
+
*/
|
115 |
+
protected function getAvailableHandle()
|
116 |
+
{
|
117 |
+
// Grab a handle that is not claimed
|
118 |
+
foreach ($this->handles as $h) {
|
119 |
+
if (!in_array($h, $this->groups, true)) {
|
120 |
+
return $h;
|
121 |
+
}
|
122 |
+
}
|
123 |
+
|
124 |
+
// All are claimed, so create one
|
125 |
+
$handle = new CurlMulti();
|
126 |
+
$handle->setEventDispatcher($this->getEventDispatcher());
|
127 |
+
$this->handles[] = $handle;
|
128 |
+
|
129 |
+
return $handle;
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Trims down unused CurlMulti handles to limit the number of open connections
|
134 |
+
*/
|
135 |
+
protected function cleanupHandles()
|
136 |
+
{
|
137 |
+
if ($diff = max(0, count($this->handles) - $this->maxHandles)) {
|
138 |
+
for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) {
|
139 |
+
if (!count($this->handles[$i])) {
|
140 |
+
unset($this->handles[$i]);
|
141 |
+
$diff--;
|
142 |
+
}
|
143 |
+
}
|
144 |
+
$this->handles = array_values($this->handles);
|
145 |
+
}
|
146 |
+
}
|
147 |
+
}
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Curl;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Class used for querying curl_version data
|
7 |
+
*/
|
8 |
+
class CurlVersion
|
9 |
+
{
|
10 |
+
/** @var array curl_version() information */
|
11 |
+
protected $version;
|
12 |
+
|
13 |
+
/** @var CurlVersion */
|
14 |
+
protected static $instance;
|
15 |
+
|
16 |
+
/** @var string Default user agent */
|
17 |
+
protected $userAgent;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @return CurlVersion
|
21 |
+
*/
|
22 |
+
public static function getInstance()
|
23 |
+
{
|
24 |
+
if (!self::$instance) {
|
25 |
+
self::$instance = new self();
|
26 |
+
}
|
27 |
+
|
28 |
+
return self::$instance;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Get all of the curl_version() data
|
33 |
+
*
|
34 |
+
* @return array
|
35 |
+
*/
|
36 |
+
public function getAll()
|
37 |
+
{
|
38 |
+
if (!$this->version) {
|
39 |
+
$this->version = curl_version();
|
40 |
+
}
|
41 |
+
|
42 |
+
return $this->version;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Get a specific type of curl information
|
47 |
+
*
|
48 |
+
* @param string $type Version information to retrieve. This value is one of:
|
49 |
+
* - version_number: cURL 24 bit version number
|
50 |
+
* - version: cURL version number, as a string
|
51 |
+
* - ssl_version_number: OpenSSL 24 bit version number
|
52 |
+
* - ssl_version: OpenSSL version number, as a string
|
53 |
+
* - libz_version: zlib version number, as a string
|
54 |
+
* - host: Information about the host where cURL was built
|
55 |
+
* - features: A bitmask of the CURL_VERSION_XXX constants
|
56 |
+
* - protocols: An array of protocols names supported by cURL
|
57 |
+
*
|
58 |
+
* @return string|float|bool if the $type is found, and false if not found
|
59 |
+
*/
|
60 |
+
public function get($type)
|
61 |
+
{
|
62 |
+
$version = $this->getAll();
|
63 |
+
|
64 |
+
return isset($version[$type]) ? $version[$type] : false;
|
65 |
+
}
|
66 |
+
}
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Curl;
|
4 |
+
|
5 |
+
use Guzzle\Http\Message\RequestInterface;
|
6 |
+
use Guzzle\Http\EntityBody;
|
7 |
+
use Guzzle\Http\Message\Response;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Mediator between curl handles and request objects
|
11 |
+
*/
|
12 |
+
class RequestMediator
|
13 |
+
{
|
14 |
+
/** @var RequestInterface */
|
15 |
+
protected $request;
|
16 |
+
|
17 |
+
/** @var bool Whether or not to emit read/write events */
|
18 |
+
protected $emitIo;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @param RequestInterface $request Request to mediate
|
22 |
+
* @param bool $emitIo Set to true to dispatch events on input and output
|
23 |
+
*/
|
24 |
+
public function __construct(RequestInterface $request, $emitIo = false)
|
25 |
+
{
|
26 |
+
$this->request = $request;
|
27 |
+
$this->emitIo = $emitIo;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Receive a response header from curl
|
32 |
+
*
|
33 |
+
* @param resource $curl Curl handle
|
34 |
+
* @param string $header Received header
|
35 |
+
*
|
36 |
+
* @return int
|
37 |
+
*/
|
38 |
+
public function receiveResponseHeader($curl, $header)
|
39 |
+
{
|
40 |
+
static $normalize = array("\r", "\n");
|
41 |
+
$length = strlen($header);
|
42 |
+
$header = str_replace($normalize, '', $header);
|
43 |
+
|
44 |
+
if (strpos($header, 'HTTP/') === 0) {
|
45 |
+
|
46 |
+
$startLine = explode(' ', $header, 3);
|
47 |
+
$code = $startLine[1];
|
48 |
+
$status = isset($startLine[2]) ? $startLine[2] : '';
|
49 |
+
|
50 |
+
// Only download the body of the response to the specified response
|
51 |
+
// body when a successful response is received.
|
52 |
+
if ($code >= 200 && $code < 300) {
|
53 |
+
$body = $this->request->getResponseBody();
|
54 |
+
} else {
|
55 |
+
$body = EntityBody::factory();
|
56 |
+
}
|
57 |
+
|
58 |
+
$response = new Response($code, null, $body);
|
59 |
+
$response->setStatus($code, $status);
|
60 |
+
$this->request->startResponse($response);
|
61 |
+
|
62 |
+
$this->request->dispatch('request.receive.status_line', array(
|
63 |
+
'request' => $this,
|
64 |
+
'line' => $header,
|
65 |
+
'status_code' => $code,
|
66 |
+
'reason_phrase' => $status
|
67 |
+
));
|
68 |
+
|
69 |
+
} elseif ($pos = strpos($header, ':')) {
|
70 |
+
$this->request->getResponse()->addHeader(
|
71 |
+
trim(substr($header, 0, $pos)),
|
72 |
+
trim(substr($header, $pos + 1))
|
73 |
+
);
|
74 |
+
}
|
75 |
+
|
76 |
+
return $length;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Received a progress notification
|
81 |
+
*
|
82 |
+
* @param int $downloadSize Total download size
|
83 |
+
* @param int $downloaded Amount of bytes downloaded
|
84 |
+
* @param int $uploadSize Total upload size
|
85 |
+
* @param int $uploaded Amount of bytes uploaded
|
86 |
+
* @param resource $handle CurlHandle object
|
87 |
+
*/
|
88 |
+
public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null)
|
89 |
+
{
|
90 |
+
$this->request->dispatch('curl.callback.progress', array(
|
91 |
+
'request' => $this->request,
|
92 |
+
'handle' => $handle,
|
93 |
+
'download_size' => $downloadSize,
|
94 |
+
'downloaded' => $downloaded,
|
95 |
+
'upload_size' => $uploadSize,
|
96 |
+
'uploaded' => $uploaded
|
97 |
+
));
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Write data to the response body of a request
|
102 |
+
*
|
103 |
+
* @param resource $curl Curl handle
|
104 |
+
* @param string $write Data that was received
|
105 |
+
*
|
106 |
+
* @return int
|
107 |
+
*/
|
108 |
+
public function writeResponseBody($curl, $write)
|
109 |
+
{
|
110 |
+
if ($this->emitIo) {
|
111 |
+
$this->request->dispatch('curl.callback.write', array(
|
112 |
+
'request' => $this->request,
|
113 |
+
'write' => $write
|
114 |
+
));
|
115 |
+
}
|
116 |
+
|
117 |
+
if ($response = $this->request->getResponse()) {
|
118 |
+
return $response->getBody()->write($write);
|
119 |
+
} else {
|
120 |
+
// Unexpected data received before response headers - abort transfer
|
121 |
+
return 0;
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Read data from the request body and send it to curl
|
127 |
+
*
|
128 |
+
* @param resource $ch Curl handle
|
129 |
+
* @param resource $fd File descriptor
|
130 |
+
* @param int $length Amount of data to read
|
131 |
+
*
|
132 |
+
* @return string
|
133 |
+
*/
|
134 |
+
public function readRequestBody($ch, $fd, $length)
|
135 |
+
{
|
136 |
+
if (!($body = $this->request->getBody())) {
|
137 |
+
return '';
|
138 |
+
}
|
139 |
+
|
140 |
+
$read = (string) $body->read($length);
|
141 |
+
if ($this->emitIo) {
|
142 |
+
$this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read));
|
143 |
+
}
|
144 |
+
|
145 |
+
return $read;
|
146 |
+
}
|
147 |
+
}
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http;
|
4 |
+
|
5 |
+
use Guzzle\Common\Version;
|
6 |
+
use Guzzle\Stream\Stream;
|
7 |
+
use Guzzle\Common\Exception\InvalidArgumentException;
|
8 |
+
use Guzzle\Http\Mimetypes;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Entity body used with an HTTP request or response
|
12 |
+
*/
|
13 |
+
class EntityBody extends Stream implements EntityBodyInterface
|
14 |
+
{
|
15 |
+
/** @var bool Content-Encoding of the entity body if known */
|
16 |
+
protected $contentEncoding = false;
|
17 |
+
|
18 |
+
/** @var callable Method to invoke for rewinding a stream */
|
19 |
+
protected $rewindFunction;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Create a new EntityBody based on the input type
|
23 |
+
*
|
24 |
+
* @param resource|string|EntityBody $resource Entity body data
|
25 |
+
* @param int $size Size of the data contained in the resource
|
26 |
+
*
|
27 |
+
* @return EntityBody
|
28 |
+
* @throws InvalidArgumentException if the $resource arg is not a resource or string
|
29 |
+
*/
|
30 |
+
public static function factory($resource = '', $size = null)
|
31 |
+
{
|
32 |
+
if ($resource instanceof EntityBodyInterface) {
|
33 |
+
return $resource;
|
34 |
+
}
|
35 |
+
|
36 |
+
switch (gettype($resource)) {
|
37 |
+
case 'string':
|
38 |
+
return self::fromString($resource);
|
39 |
+
case 'resource':
|
40 |
+
return new static($resource, $size);
|
41 |
+
case 'object':
|
42 |
+
if (method_exists($resource, '__toString')) {
|
43 |
+
return self::fromString((string) $resource);
|
44 |
+
}
|
45 |
+
break;
|
46 |
+
case 'array':
|
47 |
+
return self::fromString(http_build_query($resource));
|
48 |
+
}
|
49 |
+
|
50 |
+
throw new InvalidArgumentException('Invalid resource type');
|
51 |
+
}
|
52 |
+
|
53 |
+
public function setRewindFunction($callable)
|
54 |
+
{
|
55 |
+
if (!is_callable($callable)) {
|
56 |
+
throw new InvalidArgumentException('Must specify a callable');
|
57 |
+
}
|
58 |
+
|
59 |
+
$this->rewindFunction = $callable;
|
60 |
+
|
61 |
+
return $this;
|
62 |
+
}
|
63 |
+
|
64 |
+
public function rewind()
|
65 |
+
{
|
66 |
+
return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind();
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Create a new EntityBody from a string
|
71 |
+
*
|
72 |
+
* @param string $string String of data
|
73 |
+
*
|
74 |
+
* @return EntityBody
|
75 |
+
*/
|
76 |
+
public static function fromString($string)
|
77 |
+
{
|
78 |
+
$stream = fopen('php://temp', 'r+');
|
79 |
+
if ($string !== '') {
|
80 |
+
fwrite($stream, $string);
|
81 |
+
rewind($stream);
|
82 |
+
}
|
83 |
+
|
84 |
+
return new static($stream);
|
85 |
+
}
|
86 |
+
|
87 |
+
public function compress($filter = 'zlib.deflate')
|
88 |
+
{
|
89 |
+
$result = $this->handleCompression($filter);
|
90 |
+
$this->contentEncoding = $result ? $filter : false;
|
91 |
+
|
92 |
+
return $result;
|
93 |
+
}
|
94 |
+
|
95 |
+
public function uncompress($filter = 'zlib.inflate')
|
96 |
+
{
|
97 |
+
$offsetStart = 0;
|
98 |
+
|
99 |
+
// When inflating gzipped data, the first 10 bytes must be stripped
|
100 |
+
// if a gzip header is present
|
101 |
+
if ($filter == 'zlib.inflate') {
|
102 |
+
// @codeCoverageIgnoreStart
|
103 |
+
if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
|
104 |
+
return false;
|
105 |
+
}
|
106 |
+
// @codeCoverageIgnoreEnd
|
107 |
+
if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") {
|
108 |
+
$offsetStart = 10;
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
$this->contentEncoding = false;
|
113 |
+
|
114 |
+
return $this->handleCompression($filter, $offsetStart);
|
115 |
+
}
|
116 |
+
|
117 |
+
public function getContentLength()
|
118 |
+
{
|
119 |
+
return $this->getSize();
|
120 |
+
}
|
121 |
+
|
122 |
+
public function getContentType()
|
123 |
+
{
|
124 |
+
return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null;
|
125 |
+
}
|
126 |
+
|
127 |
+
public function getContentMd5($rawOutput = false, $base64Encode = false)
|
128 |
+
{
|
129 |
+
if ($hash = self::getHash($this, 'md5', $rawOutput)) {
|
130 |
+
return $hash && $base64Encode ? base64_encode($hash) : $hash;
|
131 |
+
} else {
|
132 |
+
return false;
|
133 |
+
}
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Calculate the MD5 hash of an entity body
|
138 |
+
*
|
139 |
+
* @param EntityBodyInterface $body Entity body to calculate the hash for
|
140 |
+
* @param bool $rawOutput Whether or not to use raw output
|
141 |
+
* @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
|
142 |
+
*
|
143 |
+
* @return bool|string Returns an MD5 string on success or FALSE on failure
|
144 |
+
* @deprecated This will be deprecated soon
|
145 |
+
* @codeCoverageIgnore
|
146 |
+
*/
|
147 |
+
public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false)
|
148 |
+
{
|
149 |
+
Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()');
|
150 |
+
return $body->getContentMd5($rawOutput, $base64Encode);
|
151 |
+
}
|
152 |
+
|
153 |
+
public function setStreamFilterContentEncoding($streamFilterContentEncoding)
|
154 |
+
{
|
155 |
+
$this->contentEncoding = $streamFilterContentEncoding;
|
156 |
+
|
157 |
+
return $this;
|
158 |
+
}
|
159 |
+
|
160 |
+
public function getContentEncoding()
|
161 |
+
{
|
162 |
+
return strtr($this->contentEncoding, array(
|
163 |
+
'zlib.deflate' => 'gzip',
|
164 |
+
'bzip2.compress' => 'compress'
|
165 |
+
)) ?: false;
|
166 |
+
}
|
167 |
+
|
168 |
+
protected function handleCompression($filter, $offsetStart = 0)
|
169 |
+
{
|
170 |
+
// @codeCoverageIgnoreStart
|
171 |
+
if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
|
172 |
+
return false;
|
173 |
+
}
|
174 |
+
// @codeCoverageIgnoreEnd
|
175 |
+
|
176 |
+
$handle = fopen('php://temp', 'r+');
|
177 |
+
$filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE);
|
178 |
+
if (!$filter) {
|
179 |
+
return false;
|
180 |
+
}
|
181 |
+
|
182 |
+
// Seek to the offset start if possible
|
183 |
+
$this->seek($offsetStart);
|
184 |
+
while ($data = fread($this->stream, 8096)) {
|
185 |
+
fwrite($handle, $data);
|
186 |
+
}
|
187 |
+
|
188 |
+
fclose($this->stream);
|
189 |
+
$this->stream = $handle;
|
190 |
+
stream_filter_remove($filter);
|
191 |
+
$stat = fstat($this->stream);
|
192 |
+
$this->size = $stat['size'];
|
193 |
+
$this->rebuildCache();
|
194 |
+
$this->seek(0);
|
195 |
+
|
196 |
+
// Remove any existing rewind function as the underlying stream has been replaced
|
197 |
+
$this->rewindFunction = null;
|
198 |
+
|
199 |
+
return true;
|
200 |
+
}
|
201 |
+
}
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http;
|
4 |
+
|
5 |
+
use Guzzle\Stream\StreamInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Entity body used with an HTTP request or response
|
9 |
+
*/
|
10 |
+
interface EntityBodyInterface extends StreamInterface
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Specify a custom callback used to rewind a non-seekable stream. This can be useful entity enclosing requests
|
14 |
+
* that are redirected.
|
15 |
+
*
|
16 |
+
* @param mixed $callable Callable to invoke to rewind a non-seekable stream. The callback must accept an
|
17 |
+
* EntityBodyInterface object, perform the rewind if possible, and return a boolean
|
18 |
+
* representing whether or not the rewind was successful.
|
19 |
+
* @return self
|
20 |
+
*/
|
21 |
+
public function setRewindFunction($callable);
|
22 |
+
|
23 |
+
/**
|
24 |
+
* If the stream is readable, compress the data in the stream using deflate compression. The uncompressed stream is
|
25 |
+
* then closed, and the compressed stream then becomes the wrapped stream.
|
26 |
+
*
|
27 |
+
* @param string $filter Compression filter
|
28 |
+
*
|
29 |
+
* @return bool Returns TRUE on success or FALSE on failure
|
30 |
+
*/
|
31 |
+
public function compress($filter = 'zlib.deflate');
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Decompress a deflated string. Once uncompressed, the uncompressed string is then used as the wrapped stream.
|
35 |
+
*
|
36 |
+
* @param string $filter De-compression filter
|
37 |
+
*
|
38 |
+
* @return bool Returns TRUE on success or FALSE on failure
|
39 |
+
*/
|
40 |
+
public function uncompress($filter = 'zlib.inflate');
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Get the Content-Length of the entity body if possible (alias of getSize)
|
44 |
+
*
|
45 |
+
* @return int|bool Returns the Content-Length or false on failure
|
46 |
+
*/
|
47 |
+
public function getContentLength();
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Guess the Content-Type of a local stream
|
51 |
+
*
|
52 |
+
* @return string|null
|
53 |
+
* @see http://www.php.net/manual/en/function.finfo-open.php
|
54 |
+
*/
|
55 |
+
public function getContentType();
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Get an MD5 checksum of the stream's contents
|
59 |
+
*
|
60 |
+
* @param bool $rawOutput Whether or not to use raw output
|
61 |
+
* @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
|
62 |
+
*
|
63 |
+
* @return bool|string Returns an MD5 string on success or FALSE on failure
|
64 |
+
*/
|
65 |
+
public function getContentMd5($rawOutput = false, $base64Encode = false);
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Get the Content-Encoding of the EntityBody
|
69 |
+
*
|
70 |
+
* @return bool|string
|
71 |
+
*/
|
72 |
+
public function getContentEncoding();
|
73 |
+
}
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Exception;
|
4 |
+
|
5 |
+
use Guzzle\Http\Message\RequestInterface;
|
6 |
+
use Guzzle\Http\Message\Response;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Http request exception thrown when a bad response is received
|
10 |
+
*/
|
11 |
+
class BadResponseException extends RequestException
|
12 |
+
{
|
13 |
+
/** @var Response */
|
14 |
+
private $response;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Factory method to create a new response exception based on the response code.
|
18 |
+
*
|
19 |
+
* @param RequestInterface $request Request
|
20 |
+
* @param Response $response Response received
|
21 |
+
*
|
22 |
+
* @return BadResponseException
|
23 |
+
*/
|
24 |
+
public static function factory(RequestInterface $request, Response $response)
|
25 |
+
{
|
26 |
+
if ($response->isClientError()) {
|
27 |
+
$label = 'Client error response';
|
28 |
+
$class = __NAMESPACE__ . '\\ClientErrorResponseException';
|
29 |
+
} elseif ($response->isServerError()) {
|
30 |
+
$label = 'Server error response';
|
31 |
+
$class = __NAMESPACE__ . '\\ServerErrorResponseException';
|
32 |
+
} else {
|
33 |
+
$label = 'Unsuccessful response';
|
34 |
+
$class = __CLASS__;
|
35 |
+
$e = new self();
|
36 |
+
}
|
37 |
+
|
38 |
+
$message = $label . PHP_EOL . implode(PHP_EOL, array(
|
39 |
+
'[status code] ' . $response->getStatusCode(),
|
40 |
+
'[reason phrase] ' . $response->getReasonPhrase(),
|
41 |
+
'[url] ' . $request->getUrl(),
|
42 |
+
));
|
43 |
+
|
44 |
+
$e = new $class($message);
|
45 |
+
$e->setResponse($response);
|
46 |
+
$e->setRequest($request);
|
47 |
+
|
48 |
+
return $e;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Set the response that caused the exception
|
53 |
+
*
|
54 |
+
* @param Response $response Response to set
|
55 |
+
*/
|
56 |
+
public function setResponse(Response $response)
|
57 |
+
{
|
58 |
+
$this->response = $response;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Get the response that caused the exception
|
63 |
+
*
|
64 |
+
* @return Response
|
65 |
+
*/
|
66 |
+
public function getResponse()
|
67 |
+
{
|
68 |
+
return $this->response;
|
69 |
+
}
|
70 |
+
}
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Exception;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Exception when a client error is encountered (4xx codes)
|
7 |
+
*/
|
8 |
+
class ClientErrorResponseException extends BadResponseException {}
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Exception;
|
4 |
+
|
5 |
+
use Guzzle\Common\Exception\RuntimeException;
|
6 |
+
|
7 |
+
class CouldNotRewindStreamException extends RuntimeException implements HttpException {}
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Exception;
|
4 |
+
|
5 |
+
use Guzzle\Http\Curl\CurlHandle;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* cURL request exception
|
9 |
+
*/
|
10 |
+
class CurlException extends RequestException
|
11 |
+
{
|
12 |
+
private $curlError;
|
13 |
+
private $curlErrorNo;
|
14 |
+
private $handle;
|
15 |
+
private $curlInfo = array();
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Set the cURL error message
|
19 |
+
*
|
20 |
+
* @param string $error Curl error
|
21 |
+
* @param int $number Curl error number
|
22 |
+
*
|
23 |
+
* @return self
|
24 |
+
*/
|
25 |
+
public function setError($error, $number)
|
26 |
+
{
|
27 |
+
$this->curlError = $error;
|
28 |
+
$this->curlErrorNo = $number;
|
29 |
+
|
30 |
+
return $this;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Set the associated curl handle
|
35 |
+
*
|
36 |
+
* @param CurlHandle $handle Curl handle
|
37 |
+
*
|
38 |
+
* @return self
|
39 |
+
*/
|
40 |
+
public function setCurlHandle(CurlHandle $handle)
|
41 |
+
{
|
42 |
+
$this->handle = $handle;
|
43 |
+
|
44 |
+
return $this;
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Get the associated cURL handle
|
49 |
+
*
|
50 |
+
* @return CurlHandle|null
|
51 |
+
*/
|
52 |
+
public function getCurlHandle()
|
53 |
+
{
|
54 |
+
return $this->handle;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Get the associated cURL error message
|
59 |
+
*
|
60 |
+
* @return string|null
|
61 |
+
*/
|
62 |
+
public function getError()
|
63 |
+
{
|
64 |
+
return $this->curlError;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Get the associated cURL error number
|
69 |
+
*
|
70 |
+
* @return int|null
|
71 |
+
*/
|
72 |
+
public function getErrorNo()
|
73 |
+
{
|
74 |
+
return $this->curlErrorNo;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Returns curl information about the transfer
|
79 |
+
*
|
80 |
+
* @return array
|
81 |
+
*/
|
82 |
+
public function getCurlInfo()
|
83 |
+
{
|
84 |
+
return $this->curlInfo;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Set curl transfer information
|
89 |
+
*
|
90 |
+
* @param array $info Array of curl transfer information
|
91 |
+
*
|
92 |
+
* @return self
|
93 |
+
* @link http://php.net/manual/en/function.curl-getinfo.php
|
94 |
+
*/
|
95 |
+
public function setCurlInfo(array $info)
|
96 |
+
{
|
97 |
+
$this->curlInfo = $info;
|
98 |
+
|
99 |
+
return $this;
|
100 |
+
}
|
101 |
+
}
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Exception;
|
4 |
+
|
5 |
+
use Guzzle\Common\Exception\GuzzleException;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Http exception interface
|
9 |
+
*/
|
10 |
+
interface HttpException extends GuzzleException {}
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Exception;
|
4 |
+
|
5 |
+
use Guzzle\Common\Exception\ExceptionCollection;
|
6 |
+
use Guzzle\Http\Message\RequestInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Exception encountered during a multi transfer
|
10 |
+
*/
|
11 |
+
class MultiTransferException extends ExceptionCollection
|
12 |
+
{
|
13 |
+
protected $successfulRequests = array();
|
14 |
+
protected $failedRequests = array();
|
15 |
+
protected $exceptionForRequest = array();
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Get all of the requests in the transfer
|
19 |
+
*
|
20 |
+
* @return array
|
21 |
+
*/
|
22 |
+
public function getAllRequests()
|
23 |
+
{
|
24 |
+
return array_merge($this->successfulRequests, $this->failedRequests);
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Add to the array of successful requests
|
29 |
+
*
|
30 |
+
* @param RequestInterface $request Successful request
|
31 |
+
*
|
32 |
+
* @return self
|
33 |
+
*/
|
34 |
+
public function addSuccessfulRequest(RequestInterface $request)
|
35 |
+
{
|
36 |
+
$this->successfulRequests[] = $request;
|
37 |
+
|
38 |
+
return $this;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Add to the array of failed requests
|
43 |
+
*
|
44 |
+
* @param RequestInterface $request Failed request
|
45 |
+
*
|
46 |
+
* @return self
|
47 |
+
*/
|
48 |
+
public function addFailedRequest(RequestInterface $request)
|
49 |
+
{
|
50 |
+
$this->failedRequests[] = $request;
|
51 |
+
|
52 |
+
return $this;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Add to the array of failed requests and associate with exceptions
|
57 |
+
*
|
58 |
+
* @param RequestInterface $request Failed request
|
59 |
+
* @param \Exception $exception Exception to add and associate with
|
60 |
+
*
|
61 |
+
* @return self
|
62 |
+
*/
|
63 |
+
public function addFailedRequestWithException(RequestInterface $request, \Exception $exception)
|
64 |
+
{
|
65 |
+
$this->add($exception)
|
66 |
+
->addFailedRequest($request)
|
67 |
+
->exceptionForRequest[spl_object_hash($request)] = $exception;
|
68 |
+
|
69 |
+
return $this;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Get the Exception that caused the given $request to fail
|
74 |
+
*
|
75 |
+
* @param RequestInterface $request Failed command
|
76 |
+
*
|
77 |
+
* @return \Exception|null
|
78 |
+
*/
|
79 |
+
public function getExceptionForFailedRequest(RequestInterface $request)
|
80 |
+
{
|
81 |
+
$oid = spl_object_hash($request);
|
82 |
+
|
83 |
+
return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null;
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Set all of the successful requests
|
88 |
+
*
|
89 |
+
* @param array Array of requests
|
90 |
+
*
|
91 |
+
* @return self
|
92 |
+
*/
|
93 |
+
public function setSuccessfulRequests(array $requests)
|
94 |
+
{
|
95 |
+
$this->successfulRequests = $requests;
|
96 |
+
|
97 |
+
return $this;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Set all of the failed requests
|
102 |
+
*
|
103 |
+
* @param array Array of requests
|
104 |
+
*
|
105 |
+
* @return self
|
106 |
+
*/
|
107 |
+
public function setFailedRequests(array $requests)
|
108 |
+
{
|
109 |
+
$this->failedRequests = $requests;
|
110 |
+
|
111 |
+
return $this;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Get an array of successful requests sent in the multi transfer
|
116 |
+
*
|
117 |
+
* @return array
|
118 |
+
*/
|
119 |
+
public function getSuccessfulRequests()
|
120 |
+
{
|
121 |
+
return $this->successfulRequests;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Get an array of failed requests sent in the multi transfer
|
126 |
+
*
|
127 |
+
* @return array
|
128 |
+
*/
|
129 |
+
public function getFailedRequests()
|
130 |
+
{
|
131 |
+
return $this->failedRequests;
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Check if the exception object contains a request
|
136 |
+
*
|
137 |
+
* @param RequestInterface $request Request to check
|
138 |
+
*
|
139 |
+
* @return bool
|
140 |
+
*/
|
141 |
+
public function containsRequest(RequestInterface $request)
|
142 |
+
{
|
143 |
+
return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true);
|
144 |
+
}
|
145 |
+
}
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Exception;
|
4 |
+
|
5 |
+
use Guzzle\Common\Exception\RuntimeException;
|
6 |
+
use Guzzle\Http\Message\RequestInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Http request exception
|
10 |
+
*/
|
11 |
+
class RequestException extends RuntimeException implements HttpException
|
12 |
+
{
|
13 |
+
/** @var RequestInterface */
|
14 |
+
protected $request;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Set the request that caused the exception
|
18 |
+
*
|
19 |
+
* @param RequestInterface $request Request to set
|
20 |
+
*
|
21 |
+
* @return RequestException
|
22 |
+
*/
|
23 |
+
public function setRequest(RequestInterface $request)
|
24 |
+
{
|
25 |
+
$this->request = $request;
|
26 |
+
|
27 |
+
return $this;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Get the request that caused the exception
|
32 |
+
*
|
33 |
+
* @return RequestInterface
|
34 |
+
*/
|
35 |
+
public function getRequest()
|
36 |
+
{
|
37 |
+
return $this->request;
|
38 |
+
}
|
39 |
+
}
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Exception;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Exception when a server error is encountered (5xx codes)
|
7 |
+
*/
|
8 |
+
class ServerErrorResponseException extends BadResponseException {}
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Exception;
|
4 |
+
|
5 |
+
class TooManyRedirectsException extends BadResponseException {}
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http;
|
4 |
+
|
5 |
+
use Guzzle\Common\Event;
|
6 |
+
use Guzzle\Common\HasDispatcherInterface;
|
7 |
+
use Symfony\Component\EventDispatcher\EventDispatcher;
|
8 |
+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
9 |
+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* EntityBody decorator that emits events for read and write methods
|
13 |
+
*/
|
14 |
+
class IoEmittingEntityBody extends AbstractEntityBodyDecorator implements HasDispatcherInterface
|
15 |
+
{
|
16 |
+
/** @var EventDispatcherInterface */
|
17 |
+
protected $eventDispatcher;
|
18 |
+
|
19 |
+
public static function getAllEvents()
|
20 |
+
{
|
21 |
+
return array('body.read', 'body.write');
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* {@inheritdoc}
|
26 |
+
* @codeCoverageIgnore
|
27 |
+
*/
|
28 |
+
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
|
29 |
+
{
|
30 |
+
$this->eventDispatcher = $eventDispatcher;
|
31 |
+
|
32 |
+
return $this;
|
33 |
+
}
|
34 |
+
|
35 |
+
public function getEventDispatcher()
|
36 |
+
{
|
37 |
+
if (!$this->eventDispatcher) {
|
38 |
+
$this->eventDispatcher = new EventDispatcher();
|
39 |
+
}
|
40 |
+
|
41 |
+
return $this->eventDispatcher;
|
42 |
+
}
|
43 |
+
|
44 |
+
public function dispatch($eventName, array $context = array())
|
45 |
+
{
|
46 |
+
return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* {@inheritdoc}
|
51 |
+
* @codeCoverageIgnore
|
52 |
+
*/
|
53 |
+
public function addSubscriber(EventSubscriberInterface $subscriber)
|
54 |
+
{
|
55 |
+
$this->getEventDispatcher()->addSubscriber($subscriber);
|
56 |
+
|
57 |
+
return $this;
|
58 |
+
}
|
59 |
+
|
60 |
+
public function read($length)
|
61 |
+
{
|
62 |
+
$event = array(
|
63 |
+
'body' => $this,
|
64 |
+
'length' => $length,
|
65 |
+
'read' => $this->body->read($length)
|
66 |
+
);
|
67 |
+
$this->dispatch('body.read', $event);
|
68 |
+
|
69 |
+
return $event['read'];
|
70 |
+
}
|
71 |
+
|
72 |
+
public function write($string)
|
73 |
+
{
|
74 |
+
$event = array(
|
75 |
+
'body' => $this,
|
76 |
+
'write' => $string,
|
77 |
+
'result' => $this->body->write($string)
|
78 |
+
);
|
79 |
+
$this->dispatch('body.write', $event);
|
80 |
+
|
81 |
+
return $event['result'];
|
82 |
+
}
|
83 |
+
}
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message;
|
4 |
+
|
5 |
+
use Guzzle\Common\Version;
|
6 |
+
use Guzzle\Common\Collection;
|
7 |
+
use Guzzle\Http\Message\Header\HeaderCollection;
|
8 |
+
use Guzzle\Http\Message\Header\HeaderFactory;
|
9 |
+
use Guzzle\Http\Message\Header\HeaderFactoryInterface;
|
10 |
+
use Guzzle\Http\Message\Header\HeaderInterface;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Abstract HTTP request/response message
|
14 |
+
*/
|
15 |
+
abstract class AbstractMessage implements MessageInterface
|
16 |
+
{
|
17 |
+
/** @var array HTTP header collection */
|
18 |
+
protected $headers;
|
19 |
+
|
20 |
+
/** @var HeaderFactoryInterface $headerFactory */
|
21 |
+
protected $headerFactory;
|
22 |
+
|
23 |
+
/** @var Collection Custom message parameters that are extendable by plugins */
|
24 |
+
protected $params;
|
25 |
+
|
26 |
+
/** @var string Message protocol */
|
27 |
+
protected $protocol = 'HTTP';
|
28 |
+
|
29 |
+
/** @var string HTTP protocol version of the message */
|
30 |
+
protected $protocolVersion = '1.1';
|
31 |
+
|
32 |
+
public function __construct()
|
33 |
+
{
|
34 |
+
$this->params = new Collection();
|
35 |
+
$this->headerFactory = new HeaderFactory();
|
36 |
+
$this->headers = new HeaderCollection();
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Set the header factory to use to create headers
|
41 |
+
*
|
42 |
+
* @param HeaderFactoryInterface $factory
|
43 |
+
*
|
44 |
+
* @return self
|
45 |
+
*/
|
46 |
+
public function setHeaderFactory(HeaderFactoryInterface $factory)
|
47 |
+
{
|
48 |
+
$this->headerFactory = $factory;
|
49 |
+
|
50 |
+
return $this;
|
51 |
+
}
|
52 |
+
|
53 |
+
public function getParams()
|
54 |
+
{
|
55 |
+
return $this->params;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function addHeader($header, $value)
|
59 |
+
{
|
60 |
+
if (isset($this->headers[$header])) {
|
61 |
+
$this->headers[$header]->add($value);
|
62 |
+
} elseif ($value instanceof HeaderInterface) {
|
63 |
+
$this->headers[$header] = $value;
|
64 |
+
} else {
|
65 |
+
$this->headers[$header] = $this->headerFactory->createHeader($header, $value);
|
66 |
+
}
|
67 |
+
|
68 |
+
return $this;
|
69 |
+
}
|
70 |
+
|
71 |
+
public function addHeaders(array $headers)
|
72 |
+
{
|
73 |
+
foreach ($headers as $key => $value) {
|
74 |
+
$this->addHeader($key, $value);
|
75 |
+
}
|
76 |
+
|
77 |
+
return $this;
|
78 |
+
}
|
79 |
+
|
80 |
+
public function getHeader($header)
|
81 |
+
{
|
82 |
+
return $this->headers[$header];
|
83 |
+
}
|
84 |
+
|
85 |
+
public function getHeaders()
|
86 |
+
{
|
87 |
+
return $this->headers;
|
88 |
+
}
|
89 |
+
|
90 |
+
public function getHeaderLines()
|
91 |
+
{
|
92 |
+
$headers = array();
|
93 |
+
foreach ($this->headers as $value) {
|
94 |
+
$headers[] = $value->getName() . ': ' . $value;
|
95 |
+
}
|
96 |
+
|
97 |
+
return $headers;
|
98 |
+
}
|
99 |
+
|
100 |
+
public function setHeader($header, $value)
|
101 |
+
{
|
102 |
+
unset($this->headers[$header]);
|
103 |
+
$this->addHeader($header, $value);
|
104 |
+
|
105 |
+
return $this;
|
106 |
+
}
|
107 |
+
|
108 |
+
public function setHeaders(array $headers)
|
109 |
+
{
|
110 |
+
$this->headers->clear();
|
111 |
+
foreach ($headers as $key => $value) {
|
112 |
+
$this->addHeader($key, $value);
|
113 |
+
}
|
114 |
+
|
115 |
+
return $this;
|
116 |
+
}
|
117 |
+
|
118 |
+
public function hasHeader($header)
|
119 |
+
{
|
120 |
+
return isset($this->headers[$header]);
|
121 |
+
}
|
122 |
+
|
123 |
+
public function removeHeader($header)
|
124 |
+
{
|
125 |
+
unset($this->headers[$header]);
|
126 |
+
|
127 |
+
return $this;
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* @deprecated Use $message->getHeader()->parseParams()
|
132 |
+
* @codeCoverageIgnore
|
133 |
+
*/
|
134 |
+
public function getTokenizedHeader($header, $token = ';')
|
135 |
+
{
|
136 |
+
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()');
|
137 |
+
if ($this->hasHeader($header)) {
|
138 |
+
$data = new Collection();
|
139 |
+
foreach ($this->getHeader($header)->parseParams() as $values) {
|
140 |
+
foreach ($values as $key => $value) {
|
141 |
+
if ($value === '') {
|
142 |
+
$data->set($data->count(), $key);
|
143 |
+
} else {
|
144 |
+
$data->add($key, $value);
|
145 |
+
}
|
146 |
+
}
|
147 |
+
}
|
148 |
+
return $data;
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* @deprecated
|
154 |
+
* @codeCoverageIgnore
|
155 |
+
*/
|
156 |
+
public function setTokenizedHeader($header, $data, $token = ';')
|
157 |
+
{
|
158 |
+
Version::warn(__METHOD__ . ' is deprecated.');
|
159 |
+
return $this;
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* @deprecated
|
164 |
+
* @codeCoverageIgnore
|
165 |
+
*/
|
166 |
+
public function getCacheControlDirective($directive)
|
167 |
+
{
|
168 |
+
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()');
|
169 |
+
if (!($header = $this->getHeader('Cache-Control'))) {
|
170 |
+
return null;
|
171 |
+
}
|
172 |
+
|
173 |
+
return $header->getDirective($directive);
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* @deprecated
|
178 |
+
* @codeCoverageIgnore
|
179 |
+
*/
|
180 |
+
public function hasCacheControlDirective($directive)
|
181 |
+
{
|
182 |
+
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()');
|
183 |
+
if ($header = $this->getHeader('Cache-Control')) {
|
184 |
+
return $header->hasDirective($directive);
|
185 |
+
} else {
|
186 |
+
return false;
|
187 |
+
}
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* @deprecated
|
192 |
+
* @codeCoverageIgnore
|
193 |
+
*/
|
194 |
+
public function addCacheControlDirective($directive, $value = true)
|
195 |
+
{
|
196 |
+
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()');
|
197 |
+
if (!($header = $this->getHeader('Cache-Control'))) {
|
198 |
+
$this->addHeader('Cache-Control', '');
|
199 |
+
$header = $this->getHeader('Cache-Control');
|
200 |
+
}
|
201 |
+
|
202 |
+
$header->addDirective($directive, $value);
|
203 |
+
|
204 |
+
return $this;
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* @deprecated
|
209 |
+
* @codeCoverageIgnore
|
210 |
+
*/
|
211 |
+
public function removeCacheControlDirective($directive)
|
212 |
+
{
|
213 |
+
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()');
|
214 |
+
if ($header = $this->getHeader('Cache-Control')) {
|
215 |
+
$header->removeDirective($directive);
|
216 |
+
}
|
217 |
+
|
218 |
+
return $this;
|
219 |
+
}
|
220 |
+
}
|
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message;
|
4 |
+
|
5 |
+
use Guzzle\Http\EntityBody;
|
6 |
+
use Guzzle\Http\EntityBodyInterface;
|
7 |
+
use Guzzle\Http\QueryString;
|
8 |
+
use Guzzle\Http\RedirectPlugin;
|
9 |
+
use Guzzle\Http\Exception\RequestException;
|
10 |
+
use Guzzle\Http\Mimetypes;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* HTTP request that sends an entity-body in the request message (POST, PUT, PATCH, DELETE)
|
14 |
+
*/
|
15 |
+
class EntityEnclosingRequest extends Request implements EntityEnclosingRequestInterface
|
16 |
+
{
|
17 |
+
/** @var int When the size of the body is greater than 1MB, then send Expect: 100-Continue */
|
18 |
+
protected $expectCutoff = 1048576;
|
19 |
+
|
20 |
+
/** @var EntityBodyInterface $body Body of the request */
|
21 |
+
protected $body;
|
22 |
+
|
23 |
+
/** @var QueryString POST fields to use in the EntityBody */
|
24 |
+
protected $postFields;
|
25 |
+
|
26 |
+
/** @var array POST files to send with the request */
|
27 |
+
protected $postFiles = array();
|
28 |
+
|
29 |
+
public function __construct($method, $url, $headers = array())
|
30 |
+
{
|
31 |
+
$this->postFields = new QueryString();
|
32 |
+
parent::__construct($method, $url, $headers);
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @return string
|
37 |
+
*/
|
38 |
+
public function __toString()
|
39 |
+
{
|
40 |
+
// Only attempt to include the POST data if it's only fields
|
41 |
+
if (count($this->postFields) && empty($this->postFiles)) {
|
42 |
+
return parent::__toString() . (string) $this->postFields;
|
43 |
+
}
|
44 |
+
|
45 |
+
return parent::__toString() . $this->body;
|
46 |
+
}
|
47 |
+
|
48 |
+
public function setState($state, array $context = array())
|
49 |
+
{
|
50 |
+
parent::setState($state, $context);
|
51 |
+
if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) {
|
52 |
+
$this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding');
|
53 |
+
}
|
54 |
+
|
55 |
+
return $this->state;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function setBody($body, $contentType = null)
|
59 |
+
{
|
60 |
+
$this->body = EntityBody::factory($body);
|
61 |
+
|
62 |
+
// Auto detect the Content-Type from the path of the request if possible
|
63 |
+
if ($contentType === null && !$this->hasHeader('Content-Type')) {
|
64 |
+
$contentType = $this->body->getContentType();
|
65 |
+
}
|
66 |
+
|
67 |
+
if ($contentType) {
|
68 |
+
$this->setHeader('Content-Type', $contentType);
|
69 |
+
}
|
70 |
+
|
71 |
+
// Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects.
|
72 |
+
if (!$this->body->isSeekable() && $this->expectCutoff !== false) {
|
73 |
+
$this->setHeader('Expect', '100-Continue');
|
74 |
+
}
|
75 |
+
|
76 |
+
// Set the Content-Length header if it can be determined
|
77 |
+
$size = $this->body->getContentLength();
|
78 |
+
if ($size !== null && $size !== false) {
|
79 |
+
$this->setHeader('Content-Length', $size);
|
80 |
+
if ($size > $this->expectCutoff) {
|
81 |
+
$this->setHeader('Expect', '100-Continue');
|
82 |
+
}
|
83 |
+
} elseif (!$this->hasHeader('Content-Length')) {
|
84 |
+
if ('1.1' == $this->protocolVersion) {
|
85 |
+
$this->setHeader('Transfer-Encoding', 'chunked');
|
86 |
+
} else {
|
87 |
+
throw new RequestException(
|
88 |
+
'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0'
|
89 |
+
);
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
return $this;
|
94 |
+
}
|
95 |
+
|
96 |
+
public function getBody()
|
97 |
+
{
|
98 |
+
return $this->body;
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header.
|
103 |
+
*
|
104 |
+
* @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data)
|
105 |
+
*
|
106 |
+
* @return self
|
107 |
+
*/
|
108 |
+
public function setExpectHeaderCutoff($size)
|
109 |
+
{
|
110 |
+
$this->expectCutoff = $size;
|
111 |
+
if ($size === false || !$this->body) {
|
112 |
+
$this->removeHeader('Expect');
|
113 |
+
} elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) {
|
114 |
+
$this->setHeader('Expect', '100-Continue');
|
115 |
+
}
|
116 |
+
|
117 |
+
return $this;
|
118 |
+
}
|
119 |
+
|
120 |
+
public function configureRedirects($strict = false, $maxRedirects = 5)
|
121 |
+
{
|
122 |
+
$this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict);
|
123 |
+
if ($maxRedirects == 0) {
|
124 |
+
$this->getParams()->set(RedirectPlugin::DISABLE, true);
|
125 |
+
} else {
|
126 |
+
$this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects);
|
127 |
+
}
|
128 |
+
|
129 |
+
return $this;
|
130 |
+
}
|
131 |
+
|
132 |
+
public function getPostField($field)
|
133 |
+
{
|
134 |
+
return $this->postFields->get($field);
|
135 |
+
}
|
136 |
+
|
137 |
+
public function getPostFields()
|
138 |
+
{
|
139 |
+
return $this->postFields;
|
140 |
+
}
|
141 |
+
|
142 |
+
public function setPostField($key, $value)
|
143 |
+
{
|
144 |
+
$this->postFields->set($key, $value);
|
145 |
+
$this->processPostFields();
|
146 |
+
|
147 |
+
return $this;
|
148 |
+
}
|
149 |
+
|
150 |
+
public function addPostFields($fields)
|
151 |
+
{
|
152 |
+
$this->postFields->merge($fields);
|
153 |
+
$this->processPostFields();
|
154 |
+
|
155 |
+
return $this;
|
156 |
+
}
|
157 |
+
|
158 |
+
public function removePostField($field)
|
159 |
+
{
|
160 |
+
$this->postFields->remove($field);
|
161 |
+
$this->processPostFields();
|
162 |
+
|
163 |
+
return $this;
|
164 |
+
}
|
165 |
+
|
166 |
+
public function getPostFiles()
|
167 |
+
{
|
168 |
+
return $this->postFiles;
|
169 |
+
}
|
170 |
+
|
171 |
+
public function getPostFile($fieldName)
|
172 |
+
{
|
173 |
+
return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null;
|
174 |
+
}
|
175 |
+
|
176 |
+
public function removePostFile($fieldName)
|
177 |
+
{
|
178 |
+
unset($this->postFiles[$fieldName]);
|
179 |
+
$this->processPostFields();
|
180 |
+
|
181 |
+
return $this;
|
182 |
+
}
|
183 |
+
|
184 |
+
public function addPostFile($field, $filename = null, $contentType = null)
|
185 |
+
{
|
186 |
+
$data = null;
|
187 |
+
|
188 |
+
if ($field instanceof PostFileInterface) {
|
189 |
+
$data = $field;
|
190 |
+
} elseif (is_array($filename)) {
|
191 |
+
// Allow multiple values to be set in a single key
|
192 |
+
foreach ($filename as $file) {
|
193 |
+
$this->addPostFile($field, $file, $contentType);
|
194 |
+
}
|
195 |
+
return $this;
|
196 |
+
} elseif (!is_string($filename)) {
|
197 |
+
throw new RequestException('The path to a file must be a string');
|
198 |
+
} elseif (!empty($filename)) {
|
199 |
+
// Adding an empty file will cause cURL to error out
|
200 |
+
$data = new PostFile($field, $filename, $contentType);
|
201 |
+
}
|
202 |
+
|
203 |
+
if ($data) {
|
204 |
+
if (!isset($this->postFiles[$data->getFieldName()])) {
|
205 |
+
$this->postFiles[$data->getFieldName()] = array($data);
|
206 |
+
} else {
|
207 |
+
$this->postFiles[$data->getFieldName()][] = $data;
|
208 |
+
}
|
209 |
+
$this->processPostFields();
|
210 |
+
}
|
211 |
+
|
212 |
+
return $this;
|
213 |
+
}
|
214 |
+
|
215 |
+
public function addPostFiles(array $files)
|
216 |
+
{
|
217 |
+
foreach ($files as $key => $file) {
|
218 |
+
if ($file instanceof PostFileInterface) {
|
219 |
+
$this->addPostFile($file, null, null, false);
|
220 |
+
} elseif (is_string($file)) {
|
221 |
+
// Convert non-associative array keys into 'file'
|
222 |
+
if (is_numeric($key)) {
|
223 |
+
$key = 'file';
|
224 |
+
}
|
225 |
+
$this->addPostFile($key, $file, null, false);
|
226 |
+
} else {
|
227 |
+
throw new RequestException('File must be a string or instance of PostFileInterface');
|
228 |
+
}
|
229 |
+
}
|
230 |
+
|
231 |
+
return $this;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Determine what type of request should be sent based on post fields
|
236 |
+
*/
|
237 |
+
protected function processPostFields()
|
238 |
+
{
|
239 |
+
if (!$this->postFiles) {
|
240 |
+
$this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED);
|
241 |
+
} else {
|
242 |
+
$this->setHeader('Content-Type', self::MULTIPART);
|
243 |
+
if ($this->expectCutoff !== false) {
|
244 |
+
$this->setHeader('Expect', '100-Continue');
|
245 |
+
}
|
246 |
+
}
|
247 |
+
}
|
248 |
+
}
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message;
|
4 |
+
|
5 |
+
use Guzzle\Http\Exception\RequestException;
|
6 |
+
use Guzzle\Http\EntityBodyInterface;
|
7 |
+
use Guzzle\Http\QueryString;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* HTTP request that sends an entity-body in the request message (POST, PUT)
|
11 |
+
*/
|
12 |
+
interface EntityEnclosingRequestInterface extends RequestInterface
|
13 |
+
{
|
14 |
+
const URL_ENCODED = 'application/x-www-form-urlencoded; charset=utf-8';
|
15 |
+
const MULTIPART = 'multipart/form-data';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Set the body of the request
|
19 |
+
*
|
20 |
+
* @param string|resource|EntityBodyInterface $body Body to use in the entity body of the request
|
21 |
+
* @param string $contentType Content-Type to set. Leave null to use an existing
|
22 |
+
* Content-Type or to guess the Content-Type
|
23 |
+
* @return self
|
24 |
+
* @throws RequestException if the protocol is < 1.1 and Content-Length can not be determined
|
25 |
+
*/
|
26 |
+
public function setBody($body, $contentType = null);
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Get the body of the request if set
|
30 |
+
*
|
31 |
+
* @return EntityBodyInterface|null
|
32 |
+
*/
|
33 |
+
public function getBody();
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Get a POST field from the request
|
37 |
+
*
|
38 |
+
* @param string $field Field to retrieve
|
39 |
+
*
|
40 |
+
* @return mixed|null
|
41 |
+
*/
|
42 |
+
public function getPostField($field);
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Get the post fields that will be used in the request
|
46 |
+
*
|
47 |
+
* @return QueryString
|
48 |
+
*/
|
49 |
+
public function getPostFields();
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Set a POST field value
|
53 |
+
*
|
54 |
+
* @param string $key Key to set
|
55 |
+
* @param string $value Value to set
|
56 |
+
*
|
57 |
+
* @return self
|
58 |
+
*/
|
59 |
+
public function setPostField($key, $value);
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Add POST fields to use in the request
|
63 |
+
*
|
64 |
+
* @param QueryString|array $fields POST fields
|
65 |
+
*
|
66 |
+
* @return self
|
67 |
+
*/
|
68 |
+
public function addPostFields($fields);
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Remove a POST field or file by name
|
72 |
+
*
|
73 |
+
* @param string $field Name of the POST field or file to remove
|
74 |
+
*
|
75 |
+
* @return self
|
76 |
+
*/
|
77 |
+
public function removePostField($field);
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Returns an associative array of POST field names to PostFileInterface objects
|
81 |
+
*
|
82 |
+
* @return array
|
83 |
+
*/
|
84 |
+
public function getPostFiles();
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Get a POST file from the request
|
88 |
+
*
|
89 |
+
* @param string $fieldName POST fields to retrieve
|
90 |
+
*
|
91 |
+
* @return array|null Returns an array wrapping an array of PostFileInterface objects
|
92 |
+
*/
|
93 |
+
public function getPostFile($fieldName);
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Remove a POST file from the request
|
97 |
+
*
|
98 |
+
* @param string $fieldName POST file field name to remove
|
99 |
+
*
|
100 |
+
* @return self
|
101 |
+
*/
|
102 |
+
public function removePostFile($fieldName);
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Add a POST file to the upload
|
106 |
+
*
|
107 |
+
* @param string $field POST field to use (e.g. file). Used to reference content from the server.
|
108 |
+
* @param string $filename Full path to the file. Do not include the @ symbol.
|
109 |
+
* @param string $contentType Optional Content-Type to add to the Content-Disposition.
|
110 |
+
* Default behavior is to guess. Set to false to not specify.
|
111 |
+
* @return self
|
112 |
+
*/
|
113 |
+
public function addPostFile($field, $filename = null, $contentType = null);
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Add POST files to use in the upload
|
117 |
+
*
|
118 |
+
* @param array $files An array of POST fields => filenames where filename can be a string or PostFileInterface
|
119 |
+
*
|
120 |
+
* @return self
|
121 |
+
*/
|
122 |
+
public function addPostFiles(array $files);
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Configure how redirects are handled for the request
|
126 |
+
*
|
127 |
+
* @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most
|
128 |
+
* browsers with follow a 301-302 redirect for a POST request with a GET request. This is
|
129 |
+
* the default behavior of Guzzle. Enable strict redirects to redirect these responses
|
130 |
+
* with a POST rather than a GET request.
|
131 |
+
* @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects.
|
132 |
+
*
|
133 |
+
* @return self
|
134 |
+
*/
|
135 |
+
public function configureRedirects($strict = false, $maxRedirects = 5);
|
136 |
+
}
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message;
|
4 |
+
|
5 |
+
use Guzzle\Common\Version;
|
6 |
+
use Guzzle\Http\Message\Header\HeaderInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Represents a header and all of the values stored by that header
|
10 |
+
*/
|
11 |
+
class Header implements HeaderInterface
|
12 |
+
{
|
13 |
+
protected $values = array();
|
14 |
+
protected $header;
|
15 |
+
protected $glue;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @param string $header Name of the header
|
19 |
+
* @param array|string $values Values of the header as an array or a scalar
|
20 |
+
* @param string $glue Glue used to combine multiple values into a string
|
21 |
+
*/
|
22 |
+
public function __construct($header, $values = array(), $glue = ',')
|
23 |
+
{
|
24 |
+
$this->header = trim($header);
|
25 |
+
$this->glue = $glue;
|
26 |
+
|
27 |
+
foreach ((array) $values as $value) {
|
28 |
+
foreach ((array) $value as $v) {
|
29 |
+
$this->values[] = $v;
|
30 |
+
}
|
31 |
+
}
|
32 |
+
}
|
33 |
+
|
34 |
+
public function __toString()
|
35 |
+
{
|
36 |
+
return implode($this->glue . ' ', $this->toArray());
|
37 |
+
}
|
38 |
+
|
39 |
+
public function add($value)
|
40 |
+
{
|
41 |
+
$this->values[] = $value;
|
42 |
+
|
43 |
+
return $this;
|
44 |
+
}
|
45 |
+
|
46 |
+
public function getName()
|
47 |
+
{
|
48 |
+
return $this->header;
|
49 |
+
}
|
50 |
+
|
51 |
+
public function setName($name)
|
52 |
+
{
|
53 |
+
$this->header = $name;
|
54 |
+
|
55 |
+
return $this;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function setGlue($glue)
|
59 |
+
{
|
60 |
+
$this->glue = $glue;
|
61 |
+
|
62 |
+
return $this;
|
63 |
+
}
|
64 |
+
|
65 |
+
public function getGlue()
|
66 |
+
{
|
67 |
+
return $this->glue;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Normalize the header to be a single header with an array of values.
|
72 |
+
*
|
73 |
+
* If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into
|
74 |
+
* multiple entries in the header.
|
75 |
+
*
|
76 |
+
* @return self
|
77 |
+
*/
|
78 |
+
public function normalize()
|
79 |
+
{
|
80 |
+
$values = $this->toArray();
|
81 |
+
|
82 |
+
for ($i = 0, $total = count($values); $i < $total; $i++) {
|
83 |
+
if (strpos($values[$i], $this->glue) !== false) {
|
84 |
+
// Explode on glue when the glue is not inside of a comma
|
85 |
+
foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) {
|
86 |
+
$values[] = trim($v);
|
87 |
+
}
|
88 |
+
unset($values[$i]);
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
$this->values = array_values($values);
|
93 |
+
|
94 |
+
return $this;
|
95 |
+
}
|
96 |
+
|
97 |
+
public function hasValue($searchValue)
|
98 |
+
{
|
99 |
+
return in_array($searchValue, $this->toArray());
|
100 |
+
}
|
101 |
+
|
102 |
+
public function removeValue($searchValue)
|
103 |
+
{
|
104 |
+
$this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) {
|
105 |
+
return $value != $searchValue;
|
106 |
+
}));
|
107 |
+
|
108 |
+
return $this;
|
109 |
+
}
|
110 |
+
|
111 |
+
public function toArray()
|
112 |
+
{
|
113 |
+
return $this->values;
|
114 |
+
}
|
115 |
+
|
116 |
+
public function count()
|
117 |
+
{
|
118 |
+
return count($this->toArray());
|
119 |
+
}
|
120 |
+
|
121 |
+
public function getIterator()
|
122 |
+
{
|
123 |
+
return new \ArrayIterator($this->toArray());
|
124 |
+
}
|
125 |
+
|
126 |
+
public function parseParams()
|
127 |
+
{
|
128 |
+
$params = $matches = array();
|
129 |
+
$callback = array($this, 'trimHeader');
|
130 |
+
|
131 |
+
// Normalize the header into a single array and iterate over all values
|
132 |
+
foreach ($this->normalize()->toArray() as $val) {
|
133 |
+
$part = array();
|
134 |
+
foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
|
135 |
+
preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches);
|
136 |
+
$pieces = array_map($callback, $matches[0]);
|
137 |
+
$part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : '';
|
138 |
+
}
|
139 |
+
$params[] = $part;
|
140 |
+
}
|
141 |
+
|
142 |
+
return $params;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* @deprecated
|
147 |
+
* @codeCoverageIgnore
|
148 |
+
*/
|
149 |
+
public function hasExactHeader($header)
|
150 |
+
{
|
151 |
+
Version::warn(__METHOD__ . ' is deprecated');
|
152 |
+
return $this->header == $header;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* @deprecated
|
157 |
+
* @codeCoverageIgnore
|
158 |
+
*/
|
159 |
+
public function raw()
|
160 |
+
{
|
161 |
+
Version::warn(__METHOD__ . ' is deprecated. Use toArray()');
|
162 |
+
return $this->toArray();
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Trim a header by removing excess spaces and wrapping quotes
|
167 |
+
*
|
168 |
+
* @param $str
|
169 |
+
*
|
170 |
+
* @return string
|
171 |
+
*/
|
172 |
+
protected function trimHeader($str)
|
173 |
+
{
|
174 |
+
static $trimmed = "\"' \n\t";
|
175 |
+
|
176 |
+
return trim($str, $trimmed);
|
177 |
+
}
|
178 |
+
}
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message\Header;
|
4 |
+
|
5 |
+
use Guzzle\Http\Message\Header;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Provides helpful functionality for Cache-Control headers
|
9 |
+
*/
|
10 |
+
class CacheControl extends Header
|
11 |
+
{
|
12 |
+
/** @var array */
|
13 |
+
protected $directives;
|
14 |
+
|
15 |
+
public function add($value)
|
16 |
+
{
|
17 |
+
parent::add($value);
|
18 |
+
$this->directives = null;
|
19 |
+
}
|
20 |
+
|
21 |
+
public function removeValue($searchValue)
|
22 |
+
{
|
23 |
+
parent::removeValue($searchValue);
|
24 |
+
$this->directives = null;
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Check if a specific cache control directive exists
|
29 |
+
*
|
30 |
+
* @param string $param Directive to retrieve
|
31 |
+
*
|
32 |
+
* @return bool
|
33 |
+
*/
|
34 |
+
public function hasDirective($param)
|
35 |
+
{
|
36 |
+
$directives = $this->getDirectives();
|
37 |
+
|
38 |
+
return isset($directives[$param]);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Get a specific cache control directive
|
43 |
+
*
|
44 |
+
* @param string $param Directive to retrieve
|
45 |
+
*
|
46 |
+
* @return string|bool|null
|
47 |
+
*/
|
48 |
+
public function getDirective($param)
|
49 |
+
{
|
50 |
+
$directives = $this->getDirectives();
|
51 |
+
|
52 |
+
return isset($directives[$param]) ? $directives[$param] : null;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Add a cache control directive
|
57 |
+
*
|
58 |
+
* @param string $param Directive to add
|
59 |
+
* @param string $value Value to set
|
60 |
+
*
|
61 |
+
* @return self
|
62 |
+
*/
|
63 |
+
public function addDirective($param, $value)
|
64 |
+
{
|
65 |
+
$directives = $this->getDirectives();
|
66 |
+
$directives[$param] = $value;
|
67 |
+
$this->updateFromDirectives($directives);
|
68 |
+
|
69 |
+
return $this;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Remove a cache control directive by name
|
74 |
+
*
|
75 |
+
* @param string $param Directive to remove
|
76 |
+
*
|
77 |
+
* @return self
|
78 |
+
*/
|
79 |
+
public function removeDirective($param)
|
80 |
+
{
|
81 |
+
$directives = $this->getDirectives();
|
82 |
+
unset($directives[$param]);
|
83 |
+
$this->updateFromDirectives($directives);
|
84 |
+
|
85 |
+
return $this;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Get an associative array of cache control directives
|
90 |
+
*
|
91 |
+
* @return array
|
92 |
+
*/
|
93 |
+
public function getDirectives()
|
94 |
+
{
|
95 |
+
if ($this->directives === null) {
|
96 |
+
$this->directives = array();
|
97 |
+
foreach ($this->parseParams() as $collection) {
|
98 |
+
foreach ($collection as $key => $value) {
|
99 |
+
$this->directives[$key] = $value === '' ? true : $value;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
return $this->directives;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Updates the header value based on the parsed directives
|
109 |
+
*
|
110 |
+
* @param array $directives Array of cache control directives
|
111 |
+
*/
|
112 |
+
protected function updateFromDirectives(array $directives)
|
113 |
+
{
|
114 |
+
$this->directives = $directives;
|
115 |
+
$this->values = array();
|
116 |
+
|
117 |
+
foreach ($directives as $key => $value) {
|
118 |
+
$this->values[] = $value === true ? $key : "{$key}={$value}";
|
119 |
+
}
|
120 |
+
}
|
121 |
+
}
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message\Header;
|
4 |
+
|
5 |
+
use Guzzle\Common\Collection;
|
6 |
+
use Guzzle\Common\ToArrayInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Provides a case-insensitive collection of headers
|
10 |
+
*/
|
11 |
+
class HeaderCollection implements \IteratorAggregate, \Countable, \ArrayAccess, ToArrayInterface
|
12 |
+
{
|
13 |
+
/** @var array */
|
14 |
+
protected $headers;
|
15 |
+
|
16 |
+
public function __construct($headers = array())
|
17 |
+
{
|
18 |
+
$this->headers = $headers;
|
19 |
+
}
|
20 |
+
|
21 |
+
public function __clone()
|
22 |
+
{
|
23 |
+
foreach ($this->headers as &$header) {
|
24 |
+
$header = clone $header;
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Clears the header collection
|
30 |
+
*/
|
31 |
+
public function clear()
|
32 |
+
{
|
33 |
+
$this->headers = array();
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Set a header on the collection
|
38 |
+
*
|
39 |
+
* @param HeaderInterface $header Header to add
|
40 |
+
*
|
41 |
+
* @return self
|
42 |
+
*/
|
43 |
+
public function add(HeaderInterface $header)
|
44 |
+
{
|
45 |
+
$this->headers[strtolower($header->getName())] = $header;
|
46 |
+
|
47 |
+
return $this;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Get an array of header objects
|
52 |
+
*
|
53 |
+
* @return array
|
54 |
+
*/
|
55 |
+
public function getAll()
|
56 |
+
{
|
57 |
+
return $this->headers;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Alias of offsetGet
|
62 |
+
*/
|
63 |
+
public function get($key)
|
64 |
+
{
|
65 |
+
return $this->offsetGet($key);
|
66 |
+
}
|
67 |
+
|
68 |
+
public function count()
|
69 |
+
{
|
70 |
+
return count($this->headers);
|
71 |
+
}
|
72 |
+
|
73 |
+
public function offsetExists($offset)
|
74 |
+
{
|
75 |
+
return isset($this->headers[strtolower($offset)]);
|
76 |
+
}
|
77 |
+
|
78 |
+
public function offsetGet($offset)
|
79 |
+
{
|
80 |
+
$l = strtolower($offset);
|
81 |
+
|
82 |
+
return isset($this->headers[$l]) ? $this->headers[$l] : null;
|
83 |
+
}
|
84 |
+
|
85 |
+
public function offsetSet($offset, $value)
|
86 |
+
{
|
87 |
+
$this->add($value);
|
88 |
+
}
|
89 |
+
|
90 |
+
public function offsetUnset($offset)
|
91 |
+
{
|
92 |
+
unset($this->headers[strtolower($offset)]);
|
93 |
+
}
|
94 |
+
|
95 |
+
public function getIterator()
|
96 |
+
{
|
97 |
+
return new \ArrayIterator($this->headers);
|
98 |
+
}
|
99 |
+
|
100 |
+
public function toArray()
|
101 |
+
{
|
102 |
+
$result = array();
|
103 |
+
foreach ($this->headers as $header) {
|
104 |
+
$result[$header->getName()] = $header->toArray();
|
105 |
+
}
|
106 |
+
|
107 |
+
return $result;
|
108 |
+
}
|
109 |
+
}
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message\Header;
|
4 |
+
|
5 |
+
use Guzzle\Http\Message\Header;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Default header factory implementation
|
9 |
+
*/
|
10 |
+
class HeaderFactory implements HeaderFactoryInterface
|
11 |
+
{
|
12 |
+
/** @var array */
|
13 |
+
protected $mapping = array(
|
14 |
+
'cache-control' => 'Guzzle\Http\Message\Header\CacheControl',
|
15 |
+
'link' => 'Guzzle\Http\Message\Header\Link',
|
16 |
+
);
|
17 |
+
|
18 |
+
public function createHeader($header, $value = null)
|
19 |
+
{
|
20 |
+
$lowercase = strtolower($header);
|
21 |
+
|
22 |
+
return isset($this->mapping[$lowercase])
|
23 |
+
? new $this->mapping[$lowercase]($header, $value)
|
24 |
+
: new Header($header, $value);
|
25 |
+
}
|
26 |
+
}
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message\Header;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Interface for creating headers
|
7 |
+
*/
|
8 |
+
interface HeaderFactoryInterface
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Create a header from a header name and a single value
|
12 |
+
*
|
13 |
+
* @param string $header Name of the header to create
|
14 |
+
* @param string $value Value to set on the header
|
15 |
+
*
|
16 |
+
* @return HeaderInterface
|
17 |
+
*/
|
18 |
+
public function createHeader($header, $value = null);
|
19 |
+
}
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message\Header;
|
4 |
+
|
5 |
+
use Guzzle\Common\ToArrayInterface;
|
6 |
+
|
7 |
+
interface HeaderInterface extends ToArrayInterface, \Countable, \IteratorAggregate
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Convert the header to a string
|
11 |
+
*
|
12 |
+
* @return string
|
13 |
+
*/
|
14 |
+
public function __toString();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Add a value to the list of header values
|
18 |
+
*
|
19 |
+
* @param string $value Value to add to the header
|
20 |
+
*
|
21 |
+
* @return self
|
22 |
+
*/
|
23 |
+
public function add($value);
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Get the name of the header
|
27 |
+
*
|
28 |
+
* @return string
|
29 |
+
*/
|
30 |
+
public function getName();
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Change the name of the header
|
34 |
+
*
|
35 |
+
* @param string $name Name to change to
|
36 |
+
*
|
37 |
+
* @return self
|
38 |
+
*/
|
39 |
+
public function setName($name);
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Change the glue used to implode the values
|
43 |
+
*
|
44 |
+
* @param string $glue Glue used to implode multiple values
|
45 |
+
*
|
46 |
+
* @return self
|
47 |
+
*/
|
48 |
+
public function setGlue($glue);
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Get the glue used to implode multiple values into a string
|
52 |
+
*
|
53 |
+
* @return string
|
54 |
+
*/
|
55 |
+
public function getGlue();
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Check if the collection of headers has a particular value
|
59 |
+
*
|
60 |
+
* @param string $searchValue Value to search for
|
61 |
+
*
|
62 |
+
* @return bool
|
63 |
+
*/
|
64 |
+
public function hasValue($searchValue);
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Remove a specific value from the header
|
68 |
+
*
|
69 |
+
* @param string $searchValue Value to remove
|
70 |
+
*
|
71 |
+
* @return self
|
72 |
+
*/
|
73 |
+
public function removeValue($searchValue);
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Parse a header containing ";" separated data into an array of associative arrays representing the header
|
77 |
+
* key value pair data of the header. When a parameter does not contain a value, but just contains a key, this
|
78 |
+
* function will inject a key with a '' string value.
|
79 |
+
*
|
80 |
+
* @return array
|
81 |
+
*/
|
82 |
+
public function parseParams();
|
83 |
+
}
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message\Header;
|
4 |
+
|
5 |
+
use Guzzle\Http\Message\Header;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Provides helpful functionality for link headers
|
9 |
+
*/
|
10 |
+
class Link extends Header
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Add a link to the header
|
14 |
+
*
|
15 |
+
* @param string $url Link URL
|
16 |
+
* @param string $rel Link rel
|
17 |
+
* @param array $params Other link parameters
|
18 |
+
*
|
19 |
+
* @return self
|
20 |
+
*/
|
21 |
+
public function addLink($url, $rel, array $params = array())
|
22 |
+
{
|
23 |
+
$values = array("<{$url}>", "rel=\"{$rel}\"");
|
24 |
+
|
25 |
+
foreach ($params as $k => $v) {
|
26 |
+
$values[] = "{$k}=\"{$v}\"";
|
27 |
+
}
|
28 |
+
|
29 |
+
return $this->add(implode('; ', $values));
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Check if a specific link exists for a given rel attribute
|
34 |
+
*
|
35 |
+
* @param string $rel rel value
|
36 |
+
*
|
37 |
+
* @return bool
|
38 |
+
*/
|
39 |
+
public function hasLink($rel)
|
40 |
+
{
|
41 |
+
return $this->getLink($rel) !== null;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Get a specific link for a given rel attribute
|
46 |
+
*
|
47 |
+
* @param string $rel Rel value
|
48 |
+
*
|
49 |
+
* @return array|null
|
50 |
+
*/
|
51 |
+
public function getLink($rel)
|
52 |
+
{
|
53 |
+
foreach ($this->getLinks() as $link) {
|
54 |
+
if (isset($link['rel']) && $link['rel'] == $rel) {
|
55 |
+
return $link;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
return null;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Get an associative array of links
|
64 |
+
*
|
65 |
+
* For example:
|
66 |
+
* Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"
|
67 |
+
*
|
68 |
+
* <code>
|
69 |
+
* var_export($response->getLinks());
|
70 |
+
* array(
|
71 |
+
* array(
|
72 |
+
* 'url' => 'http:/.../front.jpeg',
|
73 |
+
* 'rel' => 'back',
|
74 |
+
* 'type' => 'image/jpeg',
|
75 |
+
* )
|
76 |
+
* )
|
77 |
+
* </code>
|
78 |
+
*
|
79 |
+
* @return array
|
80 |
+
*/
|
81 |
+
public function getLinks()
|
82 |
+
{
|
83 |
+
$links = $this->parseParams();
|
84 |
+
|
85 |
+
foreach ($links as &$link) {
|
86 |
+
$key = key($link);
|
87 |
+
unset($link[$key]);
|
88 |
+
$link['url'] = trim($key, '<> ');
|
89 |
+
}
|
90 |
+
|
91 |
+
return $links;
|
92 |
+
}
|
93 |
+
}
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Guzzle\Http\Message;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Request and response message interface
|
7 |
+
*/
|
8 |
+
interface MessageInterface
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Get application and plugin specific parameters set on the message.
|
12 |
+
*
|
13 |
+
* @return \Guzzle\Common\Collection
|
14 |
+
*/
|
15 |
+
public function getParams();
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Add a header to an existing collection of headers.
|
19 |
+
*
|
20 |
+
* @param string $header Header name to add
|
21 |
+
* @param string $value Value of the header
|
22 |
+
*
|
23 |
+
* @return self
|
24 |
+
*/
|
25 |
+
public function addHeader($header, $value);
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Add and merge in an array of HTTP headers.
|
29 |
+
*
|
30 |
+
* @param array $headers Associative array of header data.
|
31 |
+
*
|
32 |
+
* @return self
|
33 |
+
*/
|
34 |
+
public function addHeaders(array $headers);
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Retrieve an HTTP header by name. Performs a case-insensitive search of all headers.
|
38 |
+
*
|
39 |
+
* @param string $header Header to retrieve.
|
40 |
+
*
|
41 |
+
* @return Header|null
|
42 |
+
*/
|
43 |
+
public function getHeader($header);
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Get all headers as a collection
|
47 |
+
*
|
48 |
+
* @return \Guzzle\Http\Message\Header\HeaderCollection
|
49 |
+
*/
|
50 |
+
public function getHeaders();
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Check if the specified header is present.
|
54 |
+
*
|
55 |
+
* @param string $header The header to check.
|
56 |
+
*
|
57 |
+
* @return bool
|
58 |
+
*/
|
59 |
+
public function hasHeader($header);
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Remove a specific HTTP header.
|
63 |
+
*
|
64 |
+
* @param string $header HTTP header to remove.
|
65 |
+
*
|
66 |
+
* @return self
|
67 |
+
*/
|
68 |
+
public function removeHeader($header);
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Set an HTTP header and overwrite any existing value for the header
|
72 |
+
*
|
73 |
+
* @param string $header Name of the header to set.
|
74 |
+
* @param mixed $value Value to set.
|
75 |
+
*
|
76 |
+
* @return self
|
77 |
+
*/
|
78 |
+
public function setHeader($header, $value);
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Overwrite all HTTP headers with the supplied array of headers
|
82 |
+
*
|
83 |
+
* @param array $headers Associative array of header data.
|
84 |
+
*
|
85 |
+
* @return self
|
86 |
+
*/
|
87 |
+
public function setHeaders(array $headers);
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Get an array of message header lines (e.g. ["Host: example.com", ...])
|
91 |
+
*
|
92 |
+
* @return array
|
93 |
+
*/
|
94 |
+
public function getHeaderLines();
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Get the raw message headers as a string
|
98 |
+
*
|
99 |
+
* @return string
|
100 |
+
*/
|
101 |
+
public function getRawHeaders();
|
102 |
+
}
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|