Technical Documentation: crmeb/CRMEB
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (README.md)
📝 **Project Introduction**
Open-source freedom
CRMEB open-source mall system features 100% open-source code, is licensed under the Apache-2.0 license, and can be used commercially at no cost—without any hidden fees or functional restrictions—truly enabling zero-cost deployment and unrestricted secondary development!
Technical Architecture
Adopt ThinkPHP 6 + ElementUI + UniApp Technology stack, front-end and back-end separation design, supports modular development and efficient maintenance. The front-end is compatible with WeChat Mini Programs, H5, APP, PC, and other platforms, while the back-end centrally manages data across all platforms, ensuring smooth user experience and high-concurrency performance.
Full scene coverage
Seamlessly integrate with official accounts, mini programs, H5, APP, and PC ends, enabling real-time data interoperability to help merchants operate multi-channel businesses in an all-in-one manner, meeting all-scenario e-commerce needs.
Native Marketing Engine
Built-in 20+ Core Marketing Modules (Group buying, price slashing, flash sales, coupons, points system, live streaming sales, paid memberships, tiered memberships, user recharge, distribution and viral marketing, channel codes, new user gifts, etc.), supports custom activity rules without plugins. through Home DIY Features Merchants can design the homepage of the mall with drag-and-drop functionality, allowing them to quickly build high-conversion scenarios without any technical background, achieving operational efficiency of "what you see is what you get."
Community Technical Forum
We are committed to building a developer-friendly ecosystem, open-sourcing our code, continuously updating functional modules, and adding a theme plaza. This allows merchants to quickly customize their stores by applying beautiful e-commerce themes with one click. Developers and designers can upload original themes for users to download and use, enabling creative sharing and monetization. At the same time, we welcome developers to submit optimization suggestions or contribute code. By sharing technical achievements, we can reduce the cost of redundant development in the industry and promote the sustainable development of open-source e-commerce systems.
📝 **Theme Market**
Free Download
Come to the CRMEB Theme Plaza to enjoy a vast selection of premium templates and create a personalized mall with zero cost. No professional design skills are required. A vast number of free templates are available for direct download and use. A variety of theme styles can accurately match the needs of different industries, quickly enhancing the store's image and user experience, allowing your mall to start strong visually from the beginning.
One-click import
Simplify the complex, quickly refresh the mall interface. Say goodbye to the cumbersome manual DIY configuration process of the past. With just one click to import a theme package, you can seamlessly integrate the entire theme (including page layout and global color scheme) into your system. The system automatically completes component matching and data mounting, no code needs to be written, and the preview takes effect immediately. Making the mall's renovation and upgrade as easy as changing a mobile phone wallpaper, greatly saving the time and cost of operations and development.
Theme Square Listing
Not only use it freely, but also earn money easily. You can leverage the system's powerful DIY capabilities to freely combine color schemes, layouts, and components based on existing modules, creating a unique custom theme and directly publishing it to the Theme Plaza. You will receive the corresponding revenue share when other users pay to download your work. This not only provides a direct way to monetize your design capabilities and technical expertise, but also helps build a thriving open-source marketplace ecosystem, achieving a win-win situation for creativity and value.
Theme Market:Theme Market
🔗 List of Features | 📩 Submit Feedback | 📩 Submit code | 🔗 Theme Market
docker one-click experience
# Pull and run the CRMEB Docker image
docker run -d --name crmeb -p 8080:80 ccr.ccs.tencentyun.com/crmebky_php/crmebky:latestAccess service
- Website: http://localhost:8080
- Backend: http://localhost:8080/admin (Account: admin,Password: crmeb.com)
- MySQL: localhost:3306(Account: root,Password: 123456)
- Redis: localhost:6379
Click here for detailed information Help Documentation。
🫧 Technical Features
About secondary development:
1. Code specification: follow PSR-2 naming specification, Restful standard interface, strictly layered code, complete notes, and unified error code;
2. Permission management: built-in powerful and flexible permission management, which can control every menu;
3. Development configuration: low code added configuration, system combination data module;
4. Code generation: quickly generate background menus and pages, and quickly add, delete, modify, and query;
5. Scheduled tasks: the system has 10 built-in scheduled tasks, as well as user-defined tasks. You can set the execution cycle and code by yourself, which are perfectly compatible;
6. System events: Embed 30+system event anchors, and add events on the background page;
7. Online editing: You can edit and modify the system code in the background, without logging in to the server to modify the code file, which is convenient and fast;
8. Interface management: all interface data in the system can be seen on the background page, and the interface can be debugged online;
9. Second development efficiency: form builder PHP can quickly generate forms;
10. Quick start: background interface management, background database dictionary, system file management notes, code comments, one click installation;Performance and expansion:
1. System security: system operation log, system production log, file verification, data backup;
2. High performance: support Redis cache, queue, long connection, multiple cloud storage, and cluster deployment;
3. Multilingual: support automatic identification of browser multilingual display;
4. Drive extension: support multiple payment methods, multiple SMS messages, multiple cloud storage, etc;
5. Cloud storage: cloud storage supports remote cloud storage of pictures and videos, Alibaba Cloud, Tencent Cloud, Qiniu Cloud, JD Cloud, Tianyi Cloud, Huawei Cloud
6. One number pass: general third-party extension, supporting SMS, logistics query, electronic face bill, electronic invoice, commodity collection, and business mailing📖 System Features
📖 UI Interface Display
📖 Backend Interface Display
📱 System Demo
Admin Panel: http://v6.crmeb.net/admin
Account: admin,Password: crmeb.com
H5:http://v6.crmeb.net/ (Open on mobile)
PC:http://v6.crmeb.net/ (Open on computer)
APP:http://app.crmeb.cn/bzv (Iphone can earch for CRMEB in the AppStore to download)
Theme:https://www.crmeb.com/theme (Open on computer)
I heard you want to take a look at the complete framework of the CRMEB open source project? Click here for access!
🔐 **Operating environment**
| Operating environment | Requirements |
|---|---|
| Operating System | Linux / Windows |
| WEB Service | Nginx / Apache / IIS |
| PPHP version | PHP 7.1 ~ 7.4 |
| Mysql | MySQL 5.7 ~ 8.0(Engine:InnoDB) |
| Redis | Redis(optional, file caching is used if not installed) |
| Manager | Supervisor(for managing message queues) |
| Recommended Tools | BT Panel (User-Friendly) |
| Cloud Server | Aliyun ECS / Tencent Cloud CVM / JD Cloud ECS |
| Open port | 80, 21, 8888, 888, 443, 3306, 6379(authorized object:0.0.0.0/0) |
| PHP extension | fileinfo(optional)、redis(optional) |
| Disabled functions | proc_open, pcntl_signal, pcntl_signal_dispatch, pcntl_fork, pcntl_wait, pcntl_alarm |
| Message queue | Run command:php think queue:listen --queue (Using Supervisor) |
| Long connection | Run command:sudo -u www php think workerman start --d (Command line execution) |
| Scheduled Task | Run command:php think timer start --d (ommand line execution) |
Warm reminder: Virtual space is not supported. It is recommended to use the bt panel, and the server is recommended to use JD Cloud server: Register now to enjoy an exclusive 65% discount, click here to claim it!
📺 **Development Environment and Technologies Used**
**Development environment:**
| tool | Version | Download link |
|---|---|---|
| PHP | 7.1-7.4 | PHP Download |
| MySQL | 5.7 | MySQL Website |
| Redis | 7.0 | Redis Website |
| Nginx | 1.22 | Nginx Website |
| Apache | 2.4 | Apache HTTP Server |
| Node.js | 14/18 | Node.js LTS version |
Back-end technology stack
| Technology | Name | URL |
|---|---|---|
| PHP extension library | Basic PHP runtime environment, JSON data processing, high-precision mathematical calculations, etc | https://www.php.net/ |
| topthink | ThinkPHP view template engine, CAPTCHA generation component, queue task support, database migration tool | https://www.thinkphp.cn/ |
| overtrue | WeChat ecosystem development (official account/Mini Program/Payment) | https://github.com/w7corp/easywechat |
| php-jwt | JWT Token Generation and Verification | https://github.com/firebase/php-jwt |
| var-dumper | Debug Output Tool (Formatted Variables) | https://symfony.com/doc/current/components/var_dumper.html |
| phpoffice | File processing | https://github.com/PHPOffice/PhpSpreadsheet |
| guzzlehttp|psr7 | HTTP client library, PSR-7 HTTP Message Interface Implementation | https://guzzle-cn.readthedocs.io/zh-cn/latest |
| form-builder | UI tool for quickly building forms | https://form-create.com |
| workerman | High-performance Socket server framework, scheduled timer tasks | https://www.workerman.net |
Mobile technology stack
| Technology | Name | Official website |
|---|---|---|
| uniapp | Cross-end framework | https://uniapp.dcloud.net.cn/ |
| vuex | State management library | https://vuex.vuejs.org/ |
| socket | WebSocket communication | https://socket.io/ |
| dayjs | Time processing library | https://day.js.org/ |
| animate | CSS Animation Library | https://animate.style/ |
| easy-loadimage | Image lazy loading | https://github.com/TSjianjiao/easy-loadimage |
Admin End Technology Stack
| Technology | Name | Official website |
|---|---|---|
| vue2 | Vue framework | https://v2.vuejs.org/ |
| vuex | State management library | https://vuex.vuejs.org/ |
| element-ui | UI framework | https://element.eleme.io/ |
| axios | HTTP client | https://axios-http.com/ |
| vxe-table | Advanced Table Component | https://vxetable.cn/ |
| wangeditor | Text editor | https://www.wangeditor.com/ |
| qs | Query string parsing | https://github.com/ljharb/qs |
| xlsx | Excel processing library | https://sheetjs.com/ |
| sass | CSS Preprocessor | https://sass-lang.com/ |
| prettier | Code formatting | https://prettier.io/ |
| v-viewer | Image Viewer | https://github.com/mirari/v-viewer |
PC End Technology Stack
| Technology | Name | Official website |
|---|---|---|
| nuxt | Vue framework | https://nuxtjs.org/ |
| element | UI framework | https://element.eleme.io/ |
| axios | HTTP client | https://axios-http.com/ |
| sass | CSS Preprocessor | https://sass-lang.com/ |
| cookie-universal-nuxt | Nuxt Cookie Handling | https://github.com/microcipcip/cookie-universal |
| postcss | CSS Transformation Tool | https://postcss.org/ |
| qs | Query string parsing | https://github.com/ljharb/qs |
Want a quick installation? The tutorial is here to help!
Quick one-click installation:https://doc.crmeb.com/single_open/open_v54/20366
Manual installation configuration:https://doc.crmeb.com/single_open/open_v54/20389
Docker-compose one-click deployment:https://doc.crmeb.com/single_open/open_v54/20145
Quick Installation for BT Panel:https://doc.crmeb.com/single_open/open_v54/19892
Development Documents:
Use Document:https://doc.crmeb.com/single_open/open_v54/19849
API Documentation:https://doc.crmeb.com/single_open/open_v54/21040
Data Dictionary:https://doc.crmeb.com/single_open/open_v54/20136
Code generation:https://doc.crmeb.com/single_open/open_v54/20135
Development Documents:https://doc.crmeb.com/single_open/open_v54/19851
Video tutorial:https://www.bilibili.com/video/BV1kh4y1872K/
Technical Community:https://www.crmeb.com/ask/thread/list/147
📞 CRMEB Interaction
CRMEB Open Source Technology Exchange Group (Scan the code to join the group and get the open source version interface document, product function list, and mind map!) )
Technical community! Find methods, report bugs, check official news, and win active rewards!All are [CRMEB Technology Community](https://www.crmeb.com/ask/?from=giteephp) Everything you need is available
❤️ In the name of business, safeguard the open source heart
📕 Go online in 2 hours in your WeChat mall
📕 Professional Private Domain Membership E-commerce System
📕 Create a platform that recommends high cost-effective multi-merchant system
Back to Top :fa-arrow-circle-up:
Crmeb Vendor Qiniu Php Sdk Docs Rtc README (`crmeb/vendor/qiniu/php-sdk/docs/rtc/README.md`)
Rtc Streaming Cloud Server-Side Library For PHP
Features
- Appclient
- 创建房间: client->createApp()
- 查看房间: client->getApp()
- 删除房间: client->deleteApp()
- 生成房间token: client->appToken()
Contents
Usage
App
Create a app
$ak = "gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5";
$sk = "xxxx";
$auth = new Auth($ak, $sk);
$client = new Qiniu\Rtc\AppClient($auth);
$resp=$client->createApp("901","testApp");
print_r($resp);Get an app
$ak = "gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5";
$sk = "xxxx";
$auth = new Auth($ak, $sk);
$client = new Qiniu\Rtc\AppClient($auth);
$resp=$client->getApp("deq02uhb6");
print_r($resp);Delete an app
$ak = "gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5";
$sk = "xxxx";
$auth = new Auth($ak, $sk);
$client = new Qiniu\Rtc\AppClient($auth);
$resp=$client->deleteApp("deq02uhb6");
print_r($resp);Generate an app token
$ak = "gwd_gV4gPKZZsmEOvAuNU1AcumicmuHooTfu64q5";
$sk = "xxxx";
$auth = new Auth($ak, $sk);
$client = new Qiniu\Rtc\AppClient($auth);
$resp=$client->appToken("deq02uhb6", "lfx", '1111', (time()+3600), 'user');
print_r($resp);Crmeb Vendor Google Grpc Gcp Doc GRPC Client User Guide (`crmeb/vendor/google/grpc-gcp/doc/gRPC-client-user-guide.md`)
Instructions for create a gRPC client for google cloud services
Overview
This instruction includes a step by step guide for creating a gRPC
client to test the google cloud service from an empty linux
VM, using GCE ubuntu 16.04 TLS instance.
The main steps are followed as steps below:
- Environment prerequisite
- Install protobuf plugin and gRPC-PHP/protobuf extension
- Generate client API from .proto files
- Create the client and send/receive RPC.
Environment Prerequisite
Linux
$ [sudo] apt-get install build-essential autoconf libtool pkg-config zip unzip zlib1g-devPHP
php5.5 or above, 7.0 or abovepeclcomposer
$ [sudo] apt-get install php php-dev
$ curl -sS https://getcomposer.org/installer | php
$ [sudo] mv composer.phar /usr/local/bin/composerInstall protobuf plugin and gRPC-PHP/protobuf extension
grpc_php_plugin is used to generate client API from *.proto files. Currently,
The only way to install grpc_php_plugin is to build from the gRPC source.
Install protobuf, gRPC, which will install the plugin
$ git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc
$ cd grpc
$ git submodule update --init
# install protobuf
$ cd third_party/protobuf
$ ./autogen.sh && ./configure && make -j8
$ [sudo] make install
$ [sudo] ldconfig
# install gRPC
$ cd ../..
$ make -j8
$ [sudo] make installIt will generate grpc_php_plugin under /usr/local/bin.
Install gRPC-PHP extension
$ [sudo] pecl install protobuf
$ [sudo] pecl install grpcIt will generate protobuf.so and grpc.so under PHP's extension directory.
Note gRPC-PHP extension installed by pecl doesn't work on RHEL6 system.
Generate client API from .proto files
The common way to generate the client API is to use grpc_php_plugin directly.
Since the plugin won't find the dependency by itself. It works if all your
service proto files and dependent proto files are inside one directory. The
command looks like:
$ mkdir $HOME/project
$ protoc --proto_path=./ --php_out=$HOME/project \
--grpc_out=$HOME/project \
--plugin=protoc-gen-grpc=./bins/opt/grpc_php_plugin \
path/to/your/proto_dependency_directory1/*.proto \
path/to/your/proto_dependency_directory2/*.proto \
path/to/your/proto_directory/*.protoTake Firestore service under googleapis github repo
for example. The proto files required for generating client API are
google/api/annotations.proto
google/api/http.proto
google/api/httpbody.proto
google/longrunning/operations.proto
google/rpc/code.proto
google/rpc/error_details.proto
google/rpc/status.proto
google/type/latlng.proto
google/firestore/v1beta1/firestore.proto
google/firestore/v1beta1/common.proto
google/firestore/v1beta1/query.proto
google/firestore/v1beta1/write.proto
google/firestore/v1beta1/document.protoThus the command looks like:
$ protoc --proto_path=googleapis --plugin=protoc-gen-grpc=`which grpc_php_plugin` \
--php_out=./ --grpc_out=./ google/api/annotations.proto google/api/http.proto \
google/api/httpbody.proto google/longrunning/operations.proto google/rpc/code.proto \
google/rpc/error_details.proto google/rpc/status.proto google/type/latlng.proto \
google/firestore/v1beta1/firestore.proto google/firestore/v1beta1/common.proto \
google/firestore/v1beta1/query.proto google/firestore/v1beta1/write.proto \
google/firestore/v1beta1/document.protoSince most of cloud services already publish proto files under
googleapis github repo,
you can use it's Makefile to generate the client API.
The Makefile will help you generate the client API as
well as find the dependencies. The command will simply be:
$ cd $HOME
$ mkdir project
$ git clone https://github.com/googleapis/googleapis.git
$ cd googleapis
$ make LANGUAGE=php OUTPUT=$HOME/project
# (It's okay if you see error like Please add 'syntax = "proto3";'
# to the top of your .proto file.)The client API library is generated under $HOME/project.
Take Firestore
as example, the Client API is underproject/Google/Cloud/Firestore/V1beta1/FirestoreClient.php depends on your
package name inside .proto file. An easy way to find your client is
$ find ./ -name [service_name]Client.phpCreate the client and send/receive RPC.
Now it's time to use the client API to send and receive RPCs.
$ cd $HOME/projectInstall gRPC-PHP composer library
$ vim composer.json
######## you need to change the path and service namespace.
{
"require": {
"google/cloud": "^0.52.1"
},
"autoload": {
"psr-4": {
"FireStore\\": "src/",
"Google\\Cloud\\Firestore\\V1beta1\\": "Google/Cloud/Firestore/V1beta1/"
}
}
}
########
$ composer installSet credentials file
$ vim $HOME/key.json
## Paste you credential file downloaded from your cloud project
## which you can find in APIs&Services => credentials => create credentials
## => Service account key => your credentials
$ export GOOGLE_APPLICATION_CREDENTIALS=$HOME/key.jsonImplement Service Client
Take a unary-unary RPC listDocument from FirestoreClient as example.
Create a file name ListDocumentClient.php.
- import library
require_once __DIR__ . '/vendor/autoload.php';
use Google\Cloud\Firestore\V1beta1\FirestoreClient;
use Google\Cloud\Firestore\V1beta1\ListDocumentsRequest;
use Google\Auth\ApplicationDefaultCredentials;- Google Auth
$host = "firestore.googleapis.com";
$credentials = \Grpc\ChannelCredentials::createSsl();
// WARNING: the environment variable "GOOGLE_APPLICATION_CREDENTIALS" needs to be set
$auth = ApplicationDefaultCredentials::getCredentials();
$opts = [
'credentials' => $credentials,
'update_metadata' => $auth->getUpdateMetadataFunc(),
]- Create Client
$firestoreClient = new FirestoreClient($host, $opts);- Make and receive RPC call
$argument = new ListDocumentsRequest();
$project_id = xxxxxxx;
$argument->setParent("projects/$project_id/databases/(default)/documents");
list($Response, $error) = $firestoreClient->ListDocuments($argument)->wait();- print RPC response
$documents = $Response->getDocuments();
$index = 0;
foreach($documents as $document) {
$index++;
$name = $document->getName();
echo "=> Document $index: $name\n";
$fields = $document->getFields();
foreach ($fields as $name => $value) {
echo "$name => ".$value->getStringValue()."\n";
}
}- run the script
$ php -d extension=grpc.so -d extension=protobuf.so ListDocumentClient.phpFor different kinds of RPC(unary-unary, unary-stream, stream-unary, stream-stream),
please check grpc.io PHP part
for reference.
Crmeb Vendor Guzzle Guzzle Docs Docs (`crmeb/vendor/guzzle/guzzle/docs/docs.rst`)
.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services
==================== Guzzle Documentation
Getting started
.. toctree::
:maxdepth: 1
getting-started/overview
getting-started/installation
getting-started/faq
The HTTP client
.. toctree::
:maxdepth: 2
http-client/client
http-client/request
http-client/response
http-client/entity-bodies
http-client/http-redirects
http-client/uri-templates
Plugins
.. toctree::
:maxdepth: 1
plugins/plugins-overview
plugins/creating-plugins
plugins/async-plugin
plugins/backoff-plugin
plugins/cache-plugin
plugins/cookie-plugin
plugins/curl-auth-plugin
plugins/history-plugin
plugins/log-plugin
plugins/md5-validator-plugin
plugins/mock-plugin
plugins/oauth-plugin
The web service client
.. toctree::
:maxdepth: 1
webservice-client/webservice-client
webservice-client/using-the-service-builder
webservice-client/guzzle-service-descriptions
batching/batching
iterators/resource-iterators
iterators/guzzle-iterators
Testing
.. toctree::
:maxdepth: 2
testing/unit-testing
API Docs
Read the API docs <http://guzzlephp.org/api/index.html>_
Crmeb Vendor Guzzle Guzzle Docs Index (`crmeb/vendor/guzzle/guzzle/docs/index.rst`)
.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services
.. toctree::
:hidden:
docs.rstCrmeb Vendor Monolog Monolog Doc 01 Usage (`crmeb/vendor/monolog/monolog/doc/01-usage.md`)
Using Monolog
- Installation
- Core Concepts
- Log Levels
- Configuring a logger
- Adding extra data in the records
- Leveraging channels
- Customizing the log format
Installation
Monolog is available on Packagist (monolog/monolog)
and as such installable via Composer.
composer require monolog/monologIf you do not use Composer, you can grab the code from GitHub, and use any
PSR-0 compatible autoloader (e.g. the Symfony2 ClassLoader component)
to load Monolog classes.
Core Concepts
Every Logger instance has a channel (name) and a stack of handlers. Whenever
you add a record to the logger, it traverses the handler stack. Each handler
decides whether it fully handled the record, and if so, the propagation of the
record ends there.
This allows for flexible logging setups, for example having a StreamHandler at
the bottom of the stack that will log anything to disk, and on top of that add
a MailHandler that will send emails only when an error message is logged.
Handlers also have a $bubble property which defines whether they block the
record or not if they handled it. In this example, setting the MailHandler's$bubble argument to false means that records handled by the MailHandler will
not propagate to the StreamHandler anymore.
You can create many Loggers, each defining a channel (e.g.: db, request,
router, ..) and each of them combining various handlers, which can be shared
or not. The channel is reflected in the logs and allows you to easily see or
filter records.
Each Handler also has a Formatter, a default one with settings that make sense
will be created if you don't set one. The formatters normalize and format
incoming records so that they can be used by the handlers to output useful
information.
Custom severity levels are not available. Only the eight
RFC 5424 levels (debug, info, notice,
warning, error, critical, alert, emergency) are present for basic filtering
purposes, but for sorting and other use cases that would require
flexibility, you should add Processors to the Logger that can add extra
information (tags, user ip, ..) to the records before they are handled.
Log Levels
Monolog supports the logging levels described by RFC 5424.
DEBUG (100): Detailed debug information.
INFO (200): Interesting events. Examples: User logs in, SQL logs.
NOTICE (250): Normal but significant events.
WARNING (300): Exceptional occurrences that are not errors. Examples:
Use of deprecated APIs, poor use of an API, undesirable things that are not
necessarily wrong.ERROR (400): Runtime errors that do not require immediate action but
should typically be logged and monitored.CRITICAL (500): Critical conditions. Example: Application component
unavailable, unexpected exception.ALERT (550): Action must be taken immediately. Example: Entire website
down, database unavailable, etc. This should trigger the SMS alerts and wake
you up.EMERGENCY (600): Emergency: system is unusable.
Configuring a logger
Here is a basic setup to log to a file and to firephp on the DEBUG level:
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler;
// Create the logger
$logger = new Logger('my_logger');
// Now add some handlers
$logger->pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG));
$logger->pushHandler(new FirePHPHandler());
// You can now use your logger
$logger->addInfo('My logger is now ready');Let's explain it. The first step is to create the logger instance which will
be used in your code. The argument is a channel name, which is useful when
you use several loggers (see below for more details about it).
The logger itself does not know how to handle a record. It delegates it to
some handlers. The code above registers two handlers in the stack to allow
handling records in two different ways.
Note that the FirePHPHandler is called first as it is added on top of the
stack. This allows you to temporarily add a logger with bubbling disabled if
you want to override other configured loggers.
If you use Monolog standalone and are looking for an easy way to
configure many handlers, the theorchard/monolog-cascade
can help you build complex logging configs via PHP arrays, yaml or json configs.
Adding extra data in the records
Monolog provides two different ways to add extra informations along the simple
textual message.
Using the logging context
The first way is the context, allowing to pass an array of data along the
record:
<?php
$logger->addInfo('Adding a new user', array('username' => 'Seldaek'));Simple handlers (like the StreamHandler for instance) will simply format
the array to a string but richer handlers can take advantage of the context
(FirePHP is able to display arrays in pretty way for instance).
Using processors
The second way is to add extra data for all records by using a processor.
Processors can be any callable. They will get the record as parameter and
must return it after having eventually changed the extra part of it. Let's
write a processor adding some dummy data in the record:
<?php
$logger->pushProcessor(function ($record) {
$record['extra']['dummy'] = 'Hello world!';
return $record;
});Monolog provides some built-in processors that can be used in your project.
Look at the dedicated chapter for the list.
Tip: processors can also be registered on a specific handler instead of
the logger to apply only for this handler.
Leveraging channels
Channels are a great way to identify to which part of the application a record
is related. This is useful in big applications (and is leveraged by
MonologBundle in Symfony2).
Picture two loggers sharing a handler that writes to a single log file.
Channels would allow you to identify the logger that issued every record.
You can easily grep through the log files filtering this or that channel.
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler;
// Create some handlers
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG);
$firephp = new FirePHPHandler();
// Create the main logger of the app
$logger = new Logger('my_logger');
$logger->pushHandler($stream);
$logger->pushHandler($firephp);
// Create a logger for the security-related stuff with a different channel
$securityLogger = new Logger('security');
$securityLogger->pushHandler($stream);
$securityLogger->pushHandler($firephp);
// Or clone the first one to only change the channel
$securityLogger = $logger->withName('security');Customizing the log format
In Monolog it's easy to customize the format of the logs written into files,
sockets, mails, databases and other handlers. Most of the handlers use the
$record['formatted']value to be automatically put into the log device. This value depends on the
formatter settings. You can choose between predefined formatter classes or
write your own (e.g. a multiline text file for human-readable output).
To configure a predefined formatter class, just set it as the handler's field:
// the default date format is "Y-m-d H:i:s"
$dateFormat = "Y n j, g:i a";
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
$output = "%datetime% > %level_name% > %message% %context% %extra%\n";
// finally, create a formatter
$formatter = new LineFormatter($output, $dateFormat);
// Create a handler
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG);
$stream->setFormatter($formatter);
// bind it to a logger object
$securityLogger = new Logger('security');
$securityLogger->pushHandler($stream);You may also reuse the same formatter between multiple handlers and share those
handlers between multiple loggers.
Handlers, Formatters and Processors →
Crmeb Vendor Monolog Monolog Doc 02 Handlers Formatters Processors (`crmeb/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md`)
Handlers, Formatters and Processors
Handlers
Log to files and syslog
- StreamHandler: Logs records into any PHP stream, use this for log files.
- RotatingFileHandler: Logs records to a file and creates one logfile per day.
It will also delete files older than$maxFiles. You should use
logrotate for high profile
setups though, this is just meant as a quick and dirty solution. - SyslogHandler: Logs records to the syslog.
- ErrorLogHandler: Logs records to PHP's
error_log()function.
Send alerts and emails
- NativeMailerHandler: Sends emails using PHP's
mail()function. - SwiftMailerHandler: Sends emails using a
Swift_Mailerinstance. - PushoverHandler: Sends mobile notifications via the Pushover API.
- HipChatHandler: Logs records to a HipChat chat room using its API.
- FlowdockHandler: Logs records to a Flowdock account.
- SlackHandler: Logs records to a Slack account using the Slack API.
- SlackbotHandler: Logs records to a Slack account using the Slackbot incoming hook.
- SlackWebhookHandler: Logs records to a Slack account using Slack Webhooks.
- MandrillHandler: Sends emails via the Mandrill API using a
Swift_Messageinstance. - FleepHookHandler: Logs records to a Fleep conversation using Webhooks.
- IFTTTHandler: Notifies an IFTTT trigger with the log channel, level name and message.
Log specific servers and networked logging
- SocketHandler: Logs records to sockets, use this
for UNIX and TCP sockets. See an example. - AmqpHandler: Logs records to an amqp compatible
server. Requires the php-amqp extension (1.0+). - GelfHandler: Logs records to a Graylog2 server.
- CubeHandler: Logs records to a Cube server.
- RavenHandler: Logs records to a Sentry server using
raven. - ZendMonitorHandler: Logs records to the Zend Monitor present in Zend Server.
- NewRelicHandler: Logs records to a NewRelic application.
- LogglyHandler: Logs records to a Loggly account.
- RollbarHandler: Logs records to a Rollbar account.
- SyslogUdpHandler: Logs records to a remote Syslogd server.
- LogEntriesHandler: Logs records to a LogEntries account.
- InsightOpsHandler: Logs records to a InsightOps account.
Logging in development
- FirePHPHandler: Handler for FirePHP, providing
inlineconsolemessages within FireBug. - ChromePHPHandler: Handler for ChromePHP, providing
inlineconsolemessages within Chrome. - BrowserConsoleHandler: Handler to send logs to browser's Javascript
consolewith
no browser extension required. Most browsers supportingconsoleAPI are supported. - PHPConsoleHandler: Handler for PHP Console, providing
inlineconsoleand notification popup messages within Chrome.
Log to databases
- RedisHandler: Logs records to a redis server.
- MongoDBHandler: Handler to write records in MongoDB via a
Mongo extension connection. - CouchDBHandler: Logs records to a CouchDB server.
- DoctrineCouchDBHandler: Logs records to a CouchDB server via the Doctrine CouchDB ODM.
- ElasticSearchHandler: Logs records to an Elastic Search server.
- DynamoDbHandler: Logs records to a DynamoDB table with the AWS SDK.
Wrappers / Special Handlers
- FingersCrossedHandler: A very interesting wrapper. It takes a logger as
parameter and will accumulate log records of all levels until a record
exceeds the defined severity level. At which point it delivers all records,
including those of lower severity, to the handler it wraps. This means that
until an error actually happens you will not see anything in your logs, but
when it happens you will have the full information, including debug and info
records. This provides you with all the information you need, but only when
you need it. - DeduplicationHandler: Useful if you are sending notifications or emails
when critical errors occur. It takes a logger as parameter and will
accumulate log records of all levels until the end of the request (orflush()is called). At that point it delivers all records to the handler
it wraps, but only if the records are unique over a given time period
(60seconds by default). If the records are duplicates they are simply
discarded. The main use of this is in case of critical failure like if your
database is unreachable for example all your requests will fail and that
can result in a lot of notifications being sent. Adding this handler reduces
the amount of notifications to a manageable level. - WhatFailureGroupHandler: This handler extends the GroupHandler ignoring
exceptions raised by each child handler. This allows you to ignore issues
where a remote tcp connection may have died but you do not want your entire
application to crash and may wish to continue to log to other handlers. - BufferHandler: This handler will buffer all the log records it receives
untilclose()is called at which point it will callhandleBatch()on the
handler it wraps with all the log messages at once. This is very useful to
send an email with all records at once for example instead of having one mail
for every log record. - GroupHandler: This handler groups other handlers. Every record received is
sent to all the handlers it is configured with. - FilterHandler: This handler only lets records of the given levels through
to the wrapped handler. - SamplingHandler: Wraps around another handler and lets you sample records
if you only want to store some of them. - NullHandler: Any record it can handle will be thrown away. This can be used
to put on top of an existing handler stack to disable it temporarily. - PsrHandler: Can be used to forward log records to an existing PSR-3 logger
- TestHandler: Used for testing, it records everything that is sent to it and
has accessors to read out the information. - HandlerWrapper: A simple handler wrapper you can inherit from to create
your own wrappers easily.
Formatters
- LineFormatter: Formats a log record into a one-line string.
- HtmlFormatter: Used to format log records into a human readable html table, mainly suitable for emails.
- NormalizerFormatter: Normalizes objects/resources down to strings so a record can easily be serialized/encoded.
- ScalarFormatter: Used to format log records into an associative array of scalar values.
- JsonFormatter: Encodes a log record into json.
- WildfireFormatter: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler.
- ChromePHPFormatter: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler.
- GelfMessageFormatter: Used to format log records into Gelf message instances, only useful for the GelfHandler.
- LogstashFormatter: Used to format log records into logstash event json, useful for any handler listed under inputs here.
- ElasticaFormatter: Used to format log records into an Elastica\Document object, only useful for the ElasticSearchHandler.
- LogglyFormatter: Used to format log records into Loggly messages, only useful for the LogglyHandler.
- FlowdockFormatter: Used to format log records into Flowdock messages, only useful for the FlowdockHandler.
- MongoDBFormatter: Converts \DateTime instances to \MongoDate and objects recursively to arrays, only useful with the MongoDBHandler.
Processors
- PsrLogMessageProcessor: Processes a log record's message according to PSR-3 rules, replacing
{foo}with the value from$context['foo']. - IntrospectionProcessor: Adds the line/file/class/method from which the log call originated.
- WebProcessor: Adds the current request URI, request method and client IP to a log record.
- MemoryUsageProcessor: Adds the current memory usage to a log record.
- MemoryPeakUsageProcessor: Adds the peak memory usage to a log record.
- ProcessIdProcessor: Adds the process id to a log record.
- UidProcessor: Adds a unique identifier to a log record.
- GitProcessor: Adds the current git branch and commit to a log record.
- TagProcessor: Adds an array of predefined tags to a log record.
Third Party Packages
Third party handlers, formatters and processors are
listed in the wiki. You
can also add your own there if you publish one.
← Usage | Utility classes →
Crmeb Vendor Monolog Monolog Doc 03 Utilities (`crmeb/vendor/monolog/monolog/doc/03-utilities.md`)
Utilities
- Registry: The
Monolog\Registryclass lets you configure global loggers that you
can then statically access from anywhere. It is not really a best practice but can
help in some older codebases or for ease of use. - ErrorHandler: The
Monolog\ErrorHandlerclass allows you to easily register
a Logger instance as an exception handler, error handler or fatal error handler. - SignalHandler: The
Monolog\SignalHandlerclass allows you to easily register
a Logger instance as a POSIX signal handler. - ErrorLevelActivationStrategy: Activates a FingersCrossedHandler when a certain log
level is reached. - ChannelLevelActivationStrategy: Activates a FingersCrossedHandler when a certain
log level is reached, depending on which channel received the log record.
← Handlers, Formatters and Processors | Extending Monolog →
Crmeb Vendor Monolog Monolog Doc 04 Extending (`crmeb/vendor/monolog/monolog/doc/04-extending.md`)
Extending Monolog
Monolog is fully extensible, allowing you to adapt your logger to your needs.
Writing your own handler
Monolog provides many built-in handlers. But if the one you need does not
exist, you can write it and use it in your logger. The only requirement is
to implement Monolog\Handler\HandlerInterface.
Let's write a PDOHandler to log records to a database. We will extend the
abstract class provided by Monolog to keep things DRY.
<?php
use Monolog\Logger;
use Monolog\Handler\AbstractProcessingHandler;
class PDOHandler extends AbstractProcessingHandler
{
private $initialized = false;
private $pdo;
private $statement;
public function __construct(PDO $pdo, $level = Logger::DEBUG, $bubble = true)
{
$this->pdo = $pdo;
parent::__construct($level, $bubble);
}
protected function write(array $record)
{
if (!$this->initialized) {
$this->initialize();
}
$this->statement->execute(array(
'channel' => $record['channel'],
'level' => $record['level'],
'message' => $record['formatted'],
'time' => $record['datetime']->format('U'),
));
}
private function initialize()
{
$this->pdo->exec(
'CREATE TABLE IF NOT EXISTS monolog '
.'(channel VARCHAR(255), level INTEGER, message LONGTEXT, time INTEGER UNSIGNED)'
);
$this->statement = $this->pdo->prepare(
'INSERT INTO monolog (channel, level, message, time) VALUES (:channel, :level, :message, :time)'
);
$this->initialized = true;
}
}You can now use this handler in your logger:
<?php
$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite')));
// You can now use your logger
$logger->addInfo('My logger is now ready');The Monolog\Handler\AbstractProcessingHandler class provides most of the
logic needed for the handler, including the use of processors and the formatting
of the record (which is why we use $record['formatted'] instead of $record['message']).
Crmeb Vendor Monolog Monolog Doc Sockets (`crmeb/vendor/monolog/monolog/doc/sockets.md`)
Sockets Handler
This handler allows you to write your logs to sockets using fsockopen
or pfsockopen.
Persistent sockets are mainly useful in web environments where you gain some performance not closing/opening
the connections between requests.
You can use a unix:// prefix to access unix sockets and udp:// to open UDP sockets instead of the default TCP.
Basic Example
<?php
use Monolog\Logger;
use Monolog\Handler\SocketHandler;
// Create the logger
$logger = new Logger('my_logger');
// Create the handler
$handler = new SocketHandler('unix:///var/log/httpd_app_log.socket');
$handler->setPersistent(true);
// Now add the handler
$logger->pushHandler($handler, Logger::DEBUG);
// You can now use your logger
$logger->addInfo('My logger is now ready');In this example, using syslog-ng, you should see the log on the log server:
cweb1 [2012-02-26 00:12:03] my_logger.INFO: My logger is now ready [] []Crmeb Vendor Guzzle Guzzle Docs Batching Batching (`crmeb/vendor/guzzle/guzzle/docs/batching/batching.rst`)
======== Batching
Guzzle provides a fairly generic and very customizable batching framework that allows developers to efficiently
transfer requests in parallel.
Sending requests and commands in parallel
You can send HTTP requests in parallel by passing an array of Guzzle\Http\Message\RequestInterface objects toGuzzle\Http\Client::send():
.. code-block:: php
$responses = $client->send(array(
$client->get('http://www.example.com/foo'),
$client->get('http://www.example.com/baz')
$client->get('http://www.example.com/bar')
));You can send commands in parallel by passing an array of Guzzle\Service\Command\CommandInterface objectsGuzzle\Service\Client::execute():
.. code-block:: php
$commands = $client->execute(array(
$client->getCommand('foo'),
$client->getCommand('baz'),
$client->getCommand('bar')
));These approaches work well for most use-cases. When you need more control over the requests that are sent in
parallel or you need to send a large number of requests, you need to use the functionality provided in theGuzzle\Batch namespace.
Batching overview
The batch object, Guzzle\Batch\Batch, is a queue. You add requests to the queue until you are ready to transfer
all of the requests. In order to efficiently transfer the items in the queue, the batch object delegates the
responsibility of dividing the queue into manageable parts to a divisor (Guzzle\Batch\BatchDivisorInterface).
The batch object then iterates over each array of items created by the divisor and sends them to the batch object'sGuzzle\Batch\BatchTransferInterface.
.. code-block:: php
use Guzzle\Batch\Batch;
use Guzzle\Http\BatchRequestTransfer;
// BatchRequestTransfer acts as both the divisor and transfer strategy
$transferStrategy = new BatchRequestTransfer(10);
$divisorStrategy = $transferStrategy;
$batch = new Batch($transferStrategy, $divisorStrategy);
// Add some requests to the batch queue
$batch->add($request1)
->add($request2)
->add($request3);
// Flush the queue and retrieve the flushed items
$arrayOfTransferredRequests = $batch->flush();.. note::
You might find that your transfer strategy will need to act as both the divisor and transfer strategy.Using the BatchBuilder
The Guzzle\Batch\BatchBuilder makes it easier to create batch objects. The batch builder also provides an easier
way to add additional behaviors to your batch object.
Transferring requests
The ``Guzzle\Http\BatchRequestTransfer`` class efficiently transfers HTTP requests in parallel by grouping batches of
requests by the curl_multi handle that is used to transfer the requests.
.. code-block:: php
use Guzzle\Batch\BatchBuilder;
$batch = BatchBuilder::factory()
->transferRequests(10)
->build();
Transferring commandsThe Guzzle\Service\Command\BatchCommandTransfer class efficiently transfers service commands by grouping commands
by the client that is used to transfer them. You can add commands to a batch object that are transferred by different
clients, and the batch will handle the rest.
.. code-block:: php
use Guzzle\Batch\BatchBuilder;
$batch = BatchBuilder::factory()
->transferCommands(10)
->build();
$batch->add($client->getCommand('foo'))
->add($client->getCommand('baz'))
->add($client->getCommand('bar'));
$commands = $batch->flush();Batch behaviors
You can add various behaviors to your batch that allow for more customizable transfers.
Automatically flushing a queue
Use the ``Guzzle\Batch\FlushingBatch`` decorator when you want to pump a large number of items into a batch queue and
have the queue automatically flush when the size of the queue reaches a certain threshold.
.. code-block:: php
use Guzzle\Batch\BatchBuilder;
$batch = BatchBuilder::factory()
->transferRequests(10)
->autoFlushAt(10)
->build();
Batch builder method: ``autoFlushAt($threshold)``
Notifying on flush
~~~~~~~~~~~~~~~~~~
Use the ``Guzzle\Batch\NotifyingBatch`` decorator if you want a function to be notified each time the batch queue is
flushed. This is useful when paired with the flushing batch decorator. Pass a callable to the ``notify()`` method of
a batch builder to use this decorator with the builder.
.. code-block:: php
use Guzzle\Batch\BatchBuilder;
$batch = BatchBuilder::factory()
->transferRequests(10)
->autoFlushAt(10)
->notify(function (array $transferredItems) {
echo 'Transferred ' . count($transferredItems) . "items\n";
})
->build();
Batch builder method:: ``notify(callable $callback)``
Keeping a history
~~~~~~~~~~~~~~~~~
Use the ``Guzzle\Batch\HistoryBatch`` decorator if you want to maintain a history of all the items transferred with
the batch queue.
.. code-block:: php
use Guzzle\Batch\BatchBuilder;
$batch = BatchBuilder::factory()
->transferRequests(10)
->keepHistory()
->build();
After transferring items, you can use the ``getHistory()`` of a batch to retrieve an array of transferred items. Be
sure to periodically clear the history using ``clearHistory()``.
Batch builder method: ``keepHistory()``
Exception buffering
~~~~~~~~~~~~~~~~~~~
Use the ``Guzzle\Batch\ExceptionBufferingBatch`` decorator to buffer exceptions during a transfer so that you can
transfer as many items as possible then deal with the errored batches after the transfer completes. After transfer,
use the ``getExceptions()`` method of a batch to retrieve an array of
``Guzzle\Batch\Exception\BatchTransferException`` objects. You can use these exceptions to attempt to retry the
failed batches. Be sure to clear the buffered exceptions when you are done with them by using the
``clearExceptions()`` method.
Batch builder method: ``bufferExceptions()``
---
### Crmeb Vendor Guzzle Guzzle Docs Getting Started Faq (`crmeb/vendor/guzzle/guzzle/docs/getting-started/faq.rst`)
===
FAQ
===
What should I do if I get this error: Fatal error: Maximum function nesting level of '100' reached, aborting!
-------------------------------------------------------------------------------------------------------------
You could run into this error if you have the XDebug extension installed and you execute a lot of requests in
callbacks. This error message comes specifically from the XDebug extension. PHP itself does not have a function
nesting limit. Change this setting in your php.ini to increase the limit::
xdebug.max_nesting_level = 1000
[`source <http://stackoverflow.com/a/4293870/151504>`_]
How can I speed up my client?
-----------------------------
There are several things you can do to speed up your client:
1. Utilize a C based HTTP message parser (e.g. ``Guzzle\Parser\Message\PeclHttpMessageParser``)
2. Disable operation validation by setting the ``command.disable_validation`` option to true on a command
Why am I getting a 417 error response?
--------------------------------------
This can occur for a number of reasons, but if you are sending PUT, POST, or PATCH requests with an
``Expect: 100-Continue`` header, a server that does not support this header will return a 417 response. You can work
around this by calling ``$request->removeHeader('Expect');`` after setting the entity body of a request.
---
### Crmeb Vendor Guzzle Guzzle Docs Getting Started Installation (`crmeb/vendor/guzzle/guzzle/docs/getting-started/installation.rst`)
============
Installation
============
Requirements
------------
#. PHP 5.3.3+ compiled with the cURL extension
#. A recent version of cURL 7.16.2+ compiled with OpenSSL and zlib
Installing Guzzle
-----------------
Composer
~~~~~~~~
The recommended way to install Guzzle is with `Composer <http://getcomposer.org>`_. Composer is a dependency
management tool for PHP that allows you to declare the dependencies your project needs and installs them into your
project.
.. code-block:: bash
# Install Composer
curl -sS https://getcomposer.org/installer | php
# Add Guzzle as a dependency
php composer.phar require guzzle/guzzle:~3.9
After installing, you need to require Composer's autoloader:
.. code-block:: php
require 'vendor/autoload.php';
You can find out more on how to install Composer, configure autoloading, and other best-practices for defining
dependencies at `getcomposer.org <http://getcomposer.org>`_.
Using only specific parts of Guzzle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
While you can always just rely on ``guzzle/guzzle``, Guzzle provides several smaller parts of Guzzle as individual
packages available through Composer.
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| Package name | Description |
+===============================================================================================+==========================================+
| `guzzle/common <https://packagist.org/packages/guzzle/common>`_ | Provides ``Guzzle\Common`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/http <https://packagist.org/packages/guzzle/http>`_ | Provides ``Guzzle\Http`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/parser <https://packagist.org/packages/guzzle/parser>`_ | Provides ``Guzzle\Parser`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/batch <https://packagist.org/packages/guzzle/batch>`_ | Provides ``Guzzle\Batch`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/cache <https://packagist.org/packages/guzzle/cache>`_ | Provides ``Guzzle\Cache`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/inflection <https://packagist.org/packages/guzzle/inflection>`_ | Provides ``Guzzle\Inflection`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/iterator <https://packagist.org/packages/guzzle/iterator>`_ | Provides ``Guzzle\Iterator`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/log <https://packagist.org/packages/guzzle/log>`_ | Provides ``Guzzle\Log`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin <https://packagist.org/packages/guzzle/plugin>`_ | Provides ``Guzzle\Plugin`` (all plugins) |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-async <https://packagist.org/packages/guzzle/plugin-async>`_ | Provides ``Guzzle\Plugin\Async`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-backoff <https://packagist.org/packages/guzzle/plugin-backoff>`_ | Provides ``Guzzle\Plugin\BackoffPlugin`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-cache <https://packagist.org/packages/guzzle/plugin-cache>`_ | Provides ``Guzzle\Plugin\Cache`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-cookie <https://packagist.org/packages/guzzle/plugin-cookie>`_ | Provides ``Guzzle\Plugin\Cookie`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-error-response <https://packagist.org/packages/guzzle/plugin-error-response>`_ | Provides ``Guzzle\Plugin\ErrorResponse`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-history <https://packagist.org/packages/guzzle/plugin-history>`_ | Provides ``Guzzle\Plugin\History`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-log <https://packagist.org/packages/guzzle/plugin-log>`_ | Provides ``Guzzle\Plugin\Log`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-md5 <https://packagist.org/packages/guzzle/plugin-md5>`_ | Provides ``Guzzle\Plugin\Md5`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-mock <https://packagist.org/packages/guzzle/plugin-mock>`_ | Provides ``Guzzle\Plugin\Mock`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/plugin-oauth <https://packagist.org/packages/guzzle/plugin-oauth>`_ | Provides ``Guzzle\Plugin\Oauth`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/service <https://packagist.org/packages/guzzle/service>`_ | Provides ``Guzzle\Service`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
| `guzzle/stream <https://packagist.org/packages/guzzle/stream>`_ | Provides ``Guzzle\Stream`` |
+-----------------------------------------------------------------------------------------------+------------------------------------------+
Bleeding edge
^^^^^^^^^^^^^
During your development, you can keep up with the latest changes on the master branch by setting the version
requirement for Guzzle to ``dev-master``.
.. code-block:: js
{
"require": {
"guzzle/guzzle": "dev-master"
}
}
PEAR
~~~~
Guzzle can be installed through PEAR:
.. code-block:: bash
pear channel-discover guzzlephp.org/pear
pear install guzzle/guzzle
You can install a specific version of Guzzle by providing a version number suffix:
.. code-block:: bash
pear install guzzle/guzzle-3.9.0
Contributing to Guzzle
----------------------
In order to contribute, you'll need to checkout the source from GitHub and install Guzzle's dependencies using
Composer:
.. code-block:: bash
git clone https://github.com/guzzle/guzzle.git
cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev
Guzzle is unit tested with PHPUnit. You will need to create your own phpunit.xml file in order to run the unit tests
(or just copy phpunit.xml.dist to phpunit.xml). Run the tests using the vendored PHPUnit binary:
.. code-block:: bash
vendor/bin/phpunit
You'll need to install node.js v0.5.0 or newer in order to test the cURL implementation.
Framework integrations
----------------------
Using Guzzle with Symfony
~~~~~~~~~~~~~~~~~~~~~~~~~
Bundles are available on GitHub:
- `DdeboerGuzzleBundle <https://github.com/ddeboer/GuzzleBundle>`_ for Guzzle 2
- `MisdGuzzleBundle <https://github.com/misd-service-development/guzzle-bundle>`_ for Guzzle 3
Using Guzzle with Silex
~~~~~~~~~~~~~~~~~~~~~~~
A `Guzzle Silex service provider <https://github.com/guzzle/guzzle-silex-extension>`_ is available on GitHub.
---
### Crmeb Vendor Guzzle Guzzle Docs Getting Started Overview (`crmeb/vendor/guzzle/guzzle/docs/getting-started/overview.rst`)
=================
Welcome to Guzzle
=================
What is Guzzle?
~~~~~~~~~~~~~~~
Guzzle is a PHP HTTP client and framework for building web service clients. Guzzle takes the pain out of sending HTTP
requests and the redundancy out of creating web service clients.
Features at a glance
--------------------
- All the power of cURL with a simple interface.
- Persistent connections and parallel requests.
- Streams request and response bodies
- Service descriptions for quickly building clients.
- Powered by the Symfony2 EventDispatcher.
- Use all of the code or only specific components.
- Plugins for caching, logging, OAuth, mocks, and more
- Includes a custom node.js webserver to test your clients.
- Service descriptions for defining the inputs and outputs of an API
- Resource iterators for traversing paginated resources
- Batching for sending a large number of requests as efficiently as possible
.. code-block:: php
// Really simple using a static facade
Guzzle\Http\StaticClient::mount();
$response = Guzzle::get('http://guzzlephp.org');
// More control using a client class
$client = new \Guzzle\Http\Client('http://guzzlephp.org');
$request = $client->get('/');
$response = $request->send();
License
-------
Licensed using the `MIT license <http://opensource.org/licenses/MIT>`_.
Copyright (c) 2013 Michael Dowling <https://github.com/mtdowling>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Contributing
------------
Guidelines
~~~~~~~~~~
This is still a work in progress, but there are only a few rules:
1. Guzzle follows PSR-0, PSR-1, and PSR-2
2. All pull requests must include unit tests to ensure the change works as expected and to prevent future regressions
Reporting a security vulnerabilityWe want to ensure that Guzzle is a secure HTTP client library for everyone. If you've discovered a security
vulnerability in Guzzle, we appreciate your help in disclosing it to us in aresponsible manner <http://en.wikipedia.org/wiki/Responsible_disclosure>_.
Publicly disclosing a vulnerability can put the entire community at risk. If you've discovered a security concern,
please email us at [email protected]. We'll work with you to make sure that we understand the scope of the issue,
and that we fully address your concern. We consider correspondence sent to [email protected] our highest priority,
and work to address any issues that arise as quickly as possible.
After a security vulnerability has been corrected, a security hotfix release will be deployed as soon as possible.
Crmeb Vendor Guzzle Guzzle Docs Http Client Client (`crmeb/vendor/guzzle/guzzle/docs/http-client/client.rst`)
====================== The Guzzle HTTP client
Guzzle gives PHP developers complete control over HTTP requests while utilizing HTTP/1.1 best practices. Guzzle's HTTP
functionality is a robust framework built on top of the PHP libcurl bindings <http://www.php.net/curl>_.
The three main parts of the Guzzle HTTP client are:
+--------------+-------------------------------------------------------------------------------------------------------+
| Clients | Guzzle\Http\Client (creates and sends requests, associates a response with a request) |
+--------------+-------------------------------------------------------------------------------------------------------+
| Requests | Guzzle\Http\Message\Request (requests with no body), |
| | Guzzle\Http\Message\EntityEnclosingRequest (requests with a body) |
+--------------+-------------------------------------------------------------------------------------------------------+
| Responses | Guzzle\Http\Message\Response |
+--------------+-------------------------------------------------------------------------------------------------------+
Creating a Client
Clients create requests, send requests, and set responses on a request object. When instantiating a client object,
you can pass an optional "base URL" and optional array of configuration options. A base URL is a
:doc:URI template that contains the URL of a remote server. When creating requests with a relative
URL, the base URL of a client will be merged into the request's URL.
.. code-block:: php
use Guzzle\Http\Client;
// Create a client and provide a base URL
$client = new Client('https://api.github.com');
$request = $client->get('/user');
$request->setAuth('user', 'pass');
echo $request->getUrl();
// >>> https://api.github.com/user
// You must send a request in order for the transfer to occur
$response = $request->send();
echo $response->getBody();
// >>> {"type":"User", ...
echo $response->getHeader('Content-Length');
// >>> 792
$data = $response->json();
echo $data['type'];
// >>> UserBase URLs
Notice that the URL provided to the client's ``get()`` method is relative. Relative URLs will always merge into the
base URL of the client. There are a few rules that control how the URLs are merged.
.. tip::
Guzzle follows `RFC 3986 <http://tools.ietf.org/html/rfc3986#section-5.2>`_ when merging base URLs and
relative URLs.
In the above example, we passed ``/user`` to the ``get()`` method of the client. This is a relative URL, so it will
merge into the base URL of the client-- resulting in the derived URL of ``https://api.github.com/users``.
``/user`` is a relative URL but uses an absolute path because it contains the leading slash. Absolute paths will
overwrite any existing path of the base URL. If an absolute path is provided (e.g. ``/path/to/something``), then the
path specified in the base URL of the client will be replaced with the absolute path, and the query string provided
by the relative URL will replace the query string of the base URL.
Omitting the leading slash and using relative paths will add to the path of the base URL of the client. So using a
client base URL of ``https://api.twitter.com/v1.1`` and creating a GET request with ``statuses/user_timeline.json``
will result in a URL of ``https://api.twitter.com/v1.1/statuses/user_timeline.json``. If a relative path and a query
string are provided, then the relative path will be appended to the base URL path, and the query string provided will
be merged into the query string of the base URL.
If an absolute URL is provided (e.g. ``http://httpbin.org/ip``), then the request will completely use the absolute URL
as-is without merging in any of the URL parts specified in the base URL.
Configuration optionsThe second argument of the client's constructor is an array of configuration data. This can include URI template data
or special options that alter the client's behavior:
+-------------------------------+-------------------------------------------------------------------------------------+
| request.options | Associative array of :ref:Request options <request-options> to apply to every |
| | request created by the client. |
+-------------------------------+-------------------------------------------------------------------------------------+
| redirect.disable | Disable HTTP redirects for every request created by the client. |
+-------------------------------+-------------------------------------------------------------------------------------+
| curl.options | Associative array of cURL options to apply to every request created by the client. |
| | if either the key or value of an entry in the array is a string, Guzzle will |
| | attempt to find a matching defined cURL constant automatically (e.g. |
| | "CURLOPT_PROXY" will be converted to the constant CURLOPT_PROXY). |
+-------------------------------+-------------------------------------------------------------------------------------+
| ssl.certificate_authority | Set to true to use the Guzzle bundled SSL certificate bundle (this is used by |
| | default, 'system' to use the bundle on your system, a string pointing to a file to |
| | use a specific certificate file, a string pointing to a directory to use multiple |
| | certificates, or false to disable SSL validation (not recommended). |
| | |
| | When using Guzzle inside of a phar file, the bundled SSL certificate will be |
| | extracted to your system's temp folder, and each time a client is created an MD5 |
| | check will be performed to ensure the integrity of the certificate. |
+-------------------------------+-------------------------------------------------------------------------------------+
| command.params | When using a Guzzle\Service\Client object, this is an associative array of |
| | default options to set on each command created by the client. |
+-------------------------------+-------------------------------------------------------------------------------------+
Here's an example showing how to set various configuration options, including default headers to send with each request,
default query string parameters to add to each request, a default auth scheme for each request, and a proxy to use for
each request. Values can be injected into the client's base URL using variables from the configuration array.
.. code-block:: php
use Guzzle\Http\Client;
$client = new Client('https://api.twitter.com/{version}', array(
'version' => 'v1.1',
'request.options' => array(
'headers' => array('Foo' => 'Bar'),
'query' => array('testing' => '123'),
'auth' => array('username', 'password', 'Basic|Digest|NTLM|Any'),
'proxy' => 'tcp://localhost:80'
)
));Setting a custom User-Agent
The default Guzzle User-Agent header is ``Guzzle/<Guzzle_Version> curl/<curl_version> PHP/<PHP_VERSION>``. You can
customize the User-Agent header of a client by calling the ``setUserAgent()`` method of a Client object.
.. code-block:: php
// Completely override the default User-Agent
$client->setUserAgent('Test/123');
// Prepend a string to the default User-Agent
$client->setUserAgent('Test/123', true);
Creating requests with a client
-------------------------------
A Client object exposes several methods used to create Request objects:
* Create a custom HTTP request: ``$client->createRequest($method, $uri, array $headers, $body, $options)``
* Create a GET request: ``$client->get($uri, array $headers, $options)``
* Create a HEAD request: ``$client->head($uri, array $headers, $options)``
* Create a DELETE request: ``$client->delete($uri, array $headers, $body, $options)``
* Create a POST request: ``$client->post($uri, array $headers, $postBody, $options)``
* Create a PUT request: ``$client->put($uri, array $headers, $body, $options)``
* Create a PATCH request: ``$client->patch($uri, array $headers, $body, $options)``
.. code-block:: php
use Guzzle\Http\Client;
$client = new Client('http://baseurl.com/api/v1');
// Create a GET request using Relative to base URL
// URL of the request: http://baseurl.com/api/v1/path?query=123&value=abc)
$request = $client->get('path?query=123&value=abc');
$response = $request->send();
// Create HEAD request using a relative URL with an absolute path
// URL of the request: http://baseurl.com/path?query=123&value=abc
$request = $client->head('/path?query=123&value=abc');
$response = $request->send();
// Create a DELETE request using an absolute URL
$request = $client->delete('http://www.example.com/path?query=123&value=abc');
$response = $request->send();
// Create a PUT request using the contents of a PHP stream as the body
// Specify custom HTTP headers
$request = $client->put('http://www.example.com/upload', array(
'X-Header' => 'My Header'
), fopen('http://www.test.com/', 'r'));
$response = $request->send();
// Create a POST request and add the POST files manually
$request = $client->post('http://localhost:8983/solr/update')
->addPostFiles(array('file' => '/path/to/documents.xml'));
$response = $request->send();
// Check if a resource supports the DELETE method
$supportsDelete = $client->options('/path')->send()->isMethodAllowed('DELETE');
$response = $request->send();
Client objects create Request objects using a request factory (``Guzzle\Http\Message\RequestFactoryInterface``).
You can inject a custom request factory into the Client using ``$client->setRequestFactory()``, but you can typically
rely on a Client's default request factory.
Static clients
--------------
You can use Guzzle's static client facade to more easily send simple HTTP requests.
.. code-block:: php
// Mount the client so that you can access it at \Guzzle
Guzzle\Http\StaticClient::mount();
$response = Guzzle::get('http://guzzlephp.org');
Each request method of the static client (e.g. ``get()``, ``post()`, ``put()``, etc) accepts an associative array of request
options to apply to the request.
.. code-block:: php
$response = Guzzle::post('http://test.com', array(
'headers' => array('X-Foo' => 'Bar'),
'body' => array('Test' => '123'),
'timeout' => 10
));
.. _request-options:
Request options
---------------
Request options can be specified when creating a request or in the ``request.options`` parameter of a client. These
options can control various aspects of a request including: headers to send, query string data, where the response
should be downloaded, proxies, auth, etc.
headers
~~~~~~~
Associative array of headers to apply to the request. When specified in the ``$options`` argument of a client creational
method (e.g. ``get()``, ``post()``, etc), the headers in the ``$options`` array will overwrite headers specified in the
``$headers`` array.
.. code-block:: php
$request = $client->get($url, array(), array(
'headers' => array('X-Foo' => 'Bar')
));
Headers can be specified on a client to add default headers to every request sent by a client.
.. code-block:: php
$client = new Guzzle\Http\Client();
// Set a single header using path syntax
$client->setDefaultOption('headers/X-Foo', 'Bar');
// Set all headers
$client->setDefaultOption('headers', array('X-Foo' => 'Bar'));
.. note::
In addition to setting request options when creating requests or using the ``setDefaultOption()`` method, any
default client request option can be set using a client's config object:
.. code-block:: php
$client->getConfig()->setPath('request.options/headers/X-Foo', 'Bar');
query
~~~~~
Associative array of query string parameters to the request. When specified in the ``$options`` argument of a client
creational method, the query string parameters in the ``$options`` array will overwrite query string parameters
specified in the `$url`.
.. code-block:: php
$request = $client->get($url, array(), array(
'query' => array('abc' => '123')
));
Query string parameters can be specified on a client to add default query string parameters to every request sent by a
client.
.. code-block:: php
$client = new Guzzle\Http\Client();
// Set a single query string parameter using path syntax
$client->setDefaultOption('query/abc', '123');
// Set an array of default query string parameters
$client->setDefaultOption('query', array('abc' => '123'));
body
~~~~
Sets the body of a request. The value supplied to the body option can be a ``Guzzle\Http\EntityBodyInterface``, string,
fopen resource, or array when sending POST requests. When a ``body`` request option is supplied, the option value will
overwrite the ``$body`` argument of a client creational method.
auth
~~~~
Specifies and array of HTTP authorization parameters parameters to use with the request. The array must contain the
username in index [0], the password in index [1], and can optionally contain the authentication type in index [2].
The available authentication types are: "Basic" (default), "Digest", "NTLM", or "Any".
.. code-block:: php
$request = $client->get($url, array(), array(
'auth' => array('username', 'password', 'Digest')
));
// You can add auth headers to every request of a client
$client->setDefaultOption('auth', array('username', 'password', 'Digest'));
cookies
~~~~~~~
Specifies an associative array of cookies to add to the request.
allow_redirects
~~~~~~~~~~~~~~~
Specifies whether or not the request should follow redirects. Requests will follow redirects by default. Set
``allow_redirects`` to ``false`` to disable redirects.
save_to
~~~~~~~
The ``save_to`` option specifies where the body of a response is downloaded. You can pass the path to a file, an fopen
resource, or a ``Guzzle\Http\EntityBodyInterface`` object.
See :ref:`Changing where a response is downloaded <request-set-response-body>` for more information on setting the
`save_to` option.
events
~~~~~~
The `events` option makes it easy to attach listeners to the various events emitted by a request object. The `events`
options must be an associative array mapping an event name to a Closure or array the contains a Closure and the
priority of the event.
.. code-block:: php
$request = $client->get($url, array(), array(
'events' => array(
'request.before_send' => function (\Guzzle\Common\Event $e) {
echo 'About to send ' . $e['request'];
}
)
));
// Using the static client:
Guzzle::get($url, array(
'events' => array(
'request.before_send' => function (\Guzzle\Common\Event $e) {
echo 'About to send ' . $e['request'];
}
)
));
plugins
~~~~~~~
The `plugins` options makes it easy to attach an array of plugins to a request.
.. code-block:: php
// Using the static client:
Guzzle::get($url, array(
'plugins' => array(
new Guzzle\Plugin\Cache\CachePlugin(),
new Guzzle\Plugin\Cookie\CookiePlugin()
)
));
exceptions
~~~~~~~~~~
The `exceptions` option can be used to disable throwing exceptions for unsuccessful HTTP response codes
(e.g. 404, 500, etc). Set `exceptions` to false to not throw exceptions.
params
~~~~~~
The `params` options can be used to specify an associative array of data parameters to add to a request. Note that
these are not query string parameters.
timeout / connect_timeout
~~~~~~~~~~~~~~~~~~~~~~~~~
You can specify the maximum number of seconds to allow for an entire transfer to take place before timing out using
the `timeout` request option. You can specify the maximum number of seconds to wait while trying to connect using the
`connect_timeout` request option. Set either of these options to 0 to wait indefinitely.
.. code-block:: php
$request = $client->get('http://www.example.com', array(), array(
'timeout' => 20,
'connect_timeout' => 1.5
));
verify
~~~~~~
Set to true to enable SSL certificate validation (the default), false to disable SSL certificate validation, or supply
the path to a CA bundle to enable verification using a custom certificate.
cert
~~~~
The `cert` option lets you specify a PEM formatted SSL client certificate to use with servers that require one. If the
certificate requires a password, provide an array with the password as the second item.
This would typically be used in conjunction with the `ssl_key` option.
.. code-block:: php
$request = $client->get('https://www.example.com', array(), array(
'cert' => '/etc/pki/client_certificate.pem'
)
$request = $client->get('https://www.example.com', array(), array(
'cert' => array('/etc/pki/client_certificate.pem', 's3cr3tp455w0rd')
)
ssl_key
~~~~~~~
The `ssl_key` option lets you specify a file containing your PEM formatted private key, optionally protected by a password.
Note: your password is sensitive, keep the PHP script containing it safe.
This would typically be used in conjunction with the `cert` option.
.. code-block:: php
$request = $client->get('https://www.example.com', array(), array(
'ssl_key' => '/etc/pki/private_key.pem'
)
$request = $client->get('https://www.example.com', array(), array(
'ssl_key' => array('/etc/pki/private_key.pem', 's3cr3tp455w0rd')
)
proxy
~~~~~
The `proxy` option is used to specify an HTTP proxy (e.g. `http://username:[email protected]:10`).
debug
~~~~~
The `debug` option is used to show verbose cURL output for a transfer.
stream
~~~~~~
When using a static client, you can set the `stream` option to true to return a `Guzzle\Stream\Stream` object that can
be used to pull data from a stream as needed (rather than have cURL download the entire contents of a response to a
stream all at once).
.. code-block:: php
$stream = Guzzle::get('http://guzzlephp.org', array('stream' => true));
while (!$stream->feof()) {
echo $stream->readLine();
}
Sending requests
----------------
Requests can be sent by calling the ``send()`` method of a Request object, but you can also send requests using the
``send()`` method of a Client.
.. code-block:: php
$request = $client->get('http://www.amazon.com');
$response = $client->send($request);
Sending requests in parallelThe Client's send() method accept a single Guzzle\Http\Message\RequestInterface object or an array of
RequestInterface objects. When an array is specified, the requests will be sent in parallel.
Sending many HTTP requests serially (one at a time) can cause an unnecessary delay in a script's execution. Each
request must complete before a subsequent request can be sent. By sending requests in parallel, a pool of HTTP
requests can complete at the speed of the slowest request in the pool, significantly reducing the amount of time
needed to execute multiple HTTP requests. Guzzle provides a wrapper for the curl_multi functions in PHP.
Here's an example of sending three requests in parallel using a client object:
.. code-block:: php
use Guzzle\Common\Exception\MultiTransferException;
try {
$responses = $client->send(array(
$client->get('http://www.google.com/'),
$client->head('http://www.google.com/'),
$client->get('https://www.github.com/')
));
} catch (MultiTransferException $e) {
echo "The following exceptions were encountered:\n";
foreach ($e as $exception) {
echo $exception->getMessage() . "\n";
}
echo "The following requests failed:\n";
foreach ($e->getFailedRequests() as $request) {
echo $request . "\n\n";
}
echo "The following requests succeeded:\n";
foreach ($e->getSuccessfulRequests() as $request) {
echo $request . "\n\n";
}
}If the requests succeed, an array of Guzzle\Http\Message\Response objects are returned. A single request failure
will not cause the entire pool of requests to fail. Any exceptions thrown while transferring a pool of requests will
be aggregated into a Guzzle\Common\Exception\MultiTransferException exception.
Plugins and events
Guzzle provides easy to use request plugins that add behavior to requests based on signal slot event notifications
powered by theSymfony2 Event Dispatcher component <http://symfony.com/doc/2.0/components/event_dispatcher/introduction.html>_. Any
event listener or subscriber attached to a Client object will automatically be attached to each request created by the
client.
Using the same cookie session for each request
Attach a ``Guzzle\Plugin\Cookie\CookiePlugin`` to a client which will in turn add support for cookies to every request
created by a client, and each request will use the same cookie session:
.. code-block:: php
use Guzzle\Plugin\Cookie\CookiePlugin;
use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
// Create a new cookie plugin
$cookiePlugin = new CookiePlugin(new ArrayCookieJar());
// Add the cookie plugin to the client
$client->addSubscriber($cookiePlugin);
.. _client-events:
Events emitted from a client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A ``Guzzle\Http\Client`` object emits the following events:
+------------------------------+--------------------------------------------+------------------------------------------+
| Event name | Description | Event data |
+==============================+============================================+==========================================+
| client.create_request | Called when a client creates a request | * client: The client |
| | | * request: The created request |
+------------------------------+--------------------------------------------+------------------------------------------+
.. code-block:: php
use Guzzle\Common\Event;
use Guzzle\Http\Client;
$client = new Client();
// Add a listener that will echo out requests as they are created
$client->getEventDispatcher()->addListener('client.create_request', function (Event $e) {
echo 'Client object: ' . spl_object_hash($e['client']) . "\n";
echo "Request object: {$e['request']}\n";
});
---
### Crmeb Vendor Guzzle Guzzle Docs Http Client Entity Bodies (`crmeb/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst`)
===========================
Request and response bodies
===========================
`Entity body <http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html>`_ is the term used for the body of an HTTP
message. The entity body of requests and responses is inherently a
`PHP stream <http://php.net/manual/en/book.stream.php>`_ in Guzzle. The body of the request can be either a string or
a PHP stream which are converted into a ``Guzzle\Http\EntityBody`` object using its factory method. When using a
string, the entity body is stored in a `temp PHP stream <http://www.php.net/manual/en/wrappers.php.php>`_. The use of
temp PHP streams helps to protect your application from running out of memory when sending or receiving large entity
bodies in your messages. When more than 2MB of data is stored in a temp stream, it automatically stores the data on
disk rather than in memory.
EntityBody objects provide a great deal of functionality: compression, decompression, calculate the Content-MD5,
calculate the Content-Length (when the resource is repeatable), guessing the Content-Type, and more. Guzzle doesn't
need to load an entire entity body into a string when sending or retrieving data; entity bodies are streamed when
being uploaded and downloaded.
Here's an example of gzip compressing a text file then sending the file to a URL:
.. code-block:: php
use Guzzle\Http\EntityBody;
$body = EntityBody::factory(fopen('/path/to/file.txt', 'r+'));
echo $body->read(1024);
$body->seek(0, SEEK_END);
$body->write('foo');
echo $body->ftell();
$body->rewind();
// Send a request using the body
$response = $client->put('http://localhost:8080/uploads', null, $body)->send();
The body of the request can be specified in the ``Client::put()`` or ``Client::post()`` method, or, you can specify
the body of the request by calling the ``setBody()`` method of any
``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object.
Compression
-----------
You can compress the contents of an EntityBody object using the ``compress()`` method. The compress method accepts a
filter that must match to one of the supported
`PHP stream filters <http://www.php.net/manual/en/filters.compression.php>`_ on your system (e.g. `zlib.deflate`,
``bzip2.compress``, etc). Compressing an entity body will stream the entire entity body through a stream compression
filter into a temporary PHP stream. You can uncompress an entity body using the ``uncompress()`` method and passing
the PHP stream filter to use when decompressing the stream (e.g. ``zlib.inflate``).
.. code-block:: php
use Guzzle\Http\EntityBody;
$body = EntityBody::factory(fopen('/tmp/test.txt', 'r+'));
echo $body->getSize();
// >>> 1048576
// Compress using the default zlib.deflate filter
$body->compress();
echo $body->getSize();
// >>> 314572
// Decompress the stream
$body->uncompress();
echo $body->getSize();
// >>> 1048576
Decorators
----------
Guzzle provides several EntityBody decorators that can be used to add functionality to an EntityBody at runtime.
IoEmittingEntityBody
~~~~~~~~~~~~~~~~~~~~
This decorator will emit events when data is read from a stream or written to a stream. Add an event subscriber to the
entity body's ``body.read`` or ``body.write`` methods to receive notifications when data data is transferred.
.. code-block:: php
use Guzzle\Common\Event;
use Guzzle\Http\EntityBody;
use Guzzle\Http\IoEmittingEntityBody;
$original = EntityBody::factory(fopen('/tmp/test.txt', 'r+'));
$body = new IoEmittingEntityBody($original);
// Listen for read events
$body->getEventDispatcher()->addListener('body.read', function (Event $e) {
// Grab data from the event
$entityBody = $e['body'];
// Amount of data retrieved from the body
$lengthOfData = $e['length'];
// The actual data that was read
$data = $e['read'];
});
// Listen for write events
$body->getEventDispatcher()->addListener('body.write', function (Event $e) {
// Grab data from the event
$entityBody = $e['body'];
// The data that was written
$data = $e['write'];
// The actual amount of data that was written
$data = $e['read'];
});
ReadLimitEntityBody
~~~~~~~~~~~~~~~~~~~
The ReadLimitEntityBody decorator can be used to transfer a subset or slice of an existing EntityBody object. This can
be useful for breaking a large file into smaller pieces to be sent in chunks (e.g. Amazon S3's multipart upload API).
.. code-block:: php
use Guzzle\Http\EntityBody;
use Guzzle\Http\ReadLimitEntityBody;
$original = EntityBody::factory(fopen('/tmp/test.txt', 'r+'));
echo $original->getSize();
// >>> 1048576
// Limit the size of the body to 1024 bytes and start reading from byte 2048
$body = new ReadLimitEntityBody($original, 1024, 2048);
echo $body->getSize();
// >>> 1024
echo $body->ftell();
// >>> 0
CachingEntityBody
~~~~~~~~~~~~~~~~~
The CachingEntityBody decorator is used to allow seeking over previously read bytes on non-seekable read streams. This
can be useful when transferring a non-seekable entity body fails due to needing to rewind the stream (for example,
resulting from a redirect). Data that is read from the remote stream will be buffered in a PHP temp stream so that
previously read bytes are cached first in memory, then on disk.
.. code-block:: php
use Guzzle\Http\EntityBody;
use Guzzle\Http\CachingEntityBody;
$original = EntityBody::factory(fopen('http://www.google.com', 'r'));
$body = new CachingEntityBody($original);
$body->read(1024);
echo $body->ftell();
// >>> 1024
$body->seek(0);
echo $body->ftell();
// >>> 0
---
### Crmeb Vendor Guzzle Guzzle Docs Http Client Http Redirects (`crmeb/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst`)
==============
HTTP redirects
==============
By default, Guzzle will automatically follow redirects using the non-RFC compliant implementation used by most web
browsers. This means that redirects for POST requests are followed by a GET request. You can force RFC compliance by
enabling the strict mode on a request's parameter object:
.. code-block:: php
// Set per request
$request = $client->post();
$request->getParams()->set('redirect.strict', true);
// You can set globally on a client so all requests use strict redirects
$client->getConfig()->set('request.params', array(
'redirect.strict' => true
));
By default, Guzzle will redirect up to 5 times before throwing a ``Guzzle\Http\Exception\TooManyRedirectsException``.
You can raise or lower this value using the ``redirect.max`` parameter of a request object:
.. code-block:: php
$request->getParams()->set('redirect.max', 2);
Redirect history
----------------
You can get the number of redirects of a request using the resulting response object's ``getRedirectCount()`` method.
Similar to cURL's ``effective_url`` property, Guzzle provides the effective URL, or the last redirect URL that returned
the request, in a response's ``getEffectiveUrl()`` method.
When testing or debugging, it is often useful to see a history of redirects for a particular request. This can be
achieved using the HistoryPlugin.
.. code-block:: php
$request = $client->get('/');
$history = new Guzzle\Plugin\History\HistoryPlugin();
$request->addSubscriber($history);
$response = $request->send();
// Get the last redirect URL or the URL of the request that received
// this response
echo $response->getEffectiveUrl();
// Get the number of redirects
echo $response->getRedirectCount();
// Iterate over each sent request and response
foreach ($history->getAll() as $transaction) {
// Request object
echo $transaction['request']->getUrl() . "\n";
// Response object
echo $transaction['response']->getEffectiveUrl() . "\n";
}
// Or, simply cast the HistoryPlugin to a string to view each request and response
echo $history;
Disabling redirects
-------------------
You can disable redirects on a client by passing a configuration option in the client's constructor:
.. code-block:: php
$client = new Client(null, array('redirect.disable' => true));
You can also disable redirects per request:
.. code-block:: php
$request = $client->get($url, array(), array('allow_redirects' => false));
Redirects and non-repeatable streams
------------------------------------
If you are redirected when sending data from a non-repeatable stream and some of the data has been read off of the
stream, then you will get a ``Guzzle\Http\Exception\CouldNotRewindStreamException``. You can get around this error by
adding a custom rewind method to the entity body object being sent in the request.
.. code-block:: php
$request = $client->post(
'http://httpbin.com/redirect/2',
null,
fopen('http://httpbin.com/get', 'r')
);
// Add a custom function that can be used to rewind the stream
// (reopen in this example)
$request->getBody()->setRewindFunction(function ($body) {
$body->setStream(fopen('http://httpbin.com/get', 'r'));
return true;
);
$response = $client->send();
---
### Crmeb Vendor Guzzle Guzzle Docs Http Client Request (`crmeb/vendor/guzzle/guzzle/docs/http-client/request.rst`)
=====================
Using Request objects
=====================
HTTP request messages
---------------------
Request objects are all about building an HTTP message. Each part of an HTTP request message can be set individually
using methods on the request object or set in bulk using the ``setUrl()`` method. Here's the format of an HTTP request
with each part of the request referencing the method used to change it::
PUT(a) /path(b)?query=123(c) HTTP/1.1(d)
X-Header(e): header
Content-Length(e): 4
data(f)
+-------------------------+---------------------------------------------------------------------------------+
| a. **Method** | The request method can only be set when instantiating a request |
+-------------------------+---------------------------------------------------------------------------------+
| b. **Path** | ``$request->setPath('/path');`` |
+-------------------------+---------------------------------------------------------------------------------+
| c. **Query** | ``$request->getQuery()->set('query', '123');`` |
+-------------------------+---------------------------------------------------------------------------------+
| d. **Protocol version** | ``$request->setProtocolVersion('1.1');`` |
+-------------------------+---------------------------------------------------------------------------------+
| e. **Header** | ``$request->setHeader('X-Header', 'header');`` |
+-------------------------+---------------------------------------------------------------------------------+
| f. **Entity Body** | ``$request->setBody('data'); // Only available with PUT, POST, PATCH, DELETE`` |
+-------------------------+---------------------------------------------------------------------------------+
Creating requests with a client
-------------------------------
Client objects are responsible for creating HTTP request objects.
GET requests
~~~~~~~~~~~~
`GET requests <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3>`_ are the most common form of HTTP
requests. When you visit a website in your browser, the HTML of the website is downloaded using a GET request. GET
requests are idempotent requests that are typically used to download content (an entity) identified by a request URL.
.. code-block:: php
use Guzzle\Http\Client;
$client = new Client();
// Create a request that has a query string and an X-Foo header
$request = $client->get('http://www.amazon.com?a=1', array('X-Foo' => 'Bar'));
// Send the request and get the response
$response = $request->send();
You can change where the body of a response is downloaded on any request using the
``$request->setResponseBody(string|EntityBodyInterface|resource)`` method of a request. You can also set the ``save_to``
option of a request:
.. code-block:: php
// Send the response body to a file
$request = $client->get('http://test.com', array(), array('save_to' => '/path/to/file'));
// Send the response body to an fopen resource
$request = $client->get('http://test.com', array(), array('save_to' => fopen('/path/to/file', 'w')));
HEAD requests
~~~~~~~~~~~~~
`HEAD requests <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4>`_ work exactly like GET requests except
that they do not actually download the response body (entity) of the response message. HEAD requests are useful for
retrieving meta information about an entity identified by a Request-URI.
.. code-block:: php
$client = new Guzzle\Http\Client();
$request = $client->head('http://www.amazon.com');
$response = $request->send();
echo $response->getContentLength();
// >>> Will output the Content-Length header value
DELETE requests
~~~~~~~~~~~~~~~
A `DELETE method <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7>`_ requests that the origin server
delete the resource identified by the Request-URI.
.. code-block:: php
$client = new Guzzle\Http\Client();
$request = $client->delete('http://example.com');
$response = $request->send();
POST requests
~~~~~~~~~~~~~
While `POST requests <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5>`_ can be used for a number of
reasons, POST requests are often used when submitting HTML form data to a website. POST requests can include an entity
body in the HTTP request.
POST requests in Guzzle are sent with an ``application/x-www-form-urlencoded`` Content-Type header if POST fields are
present but no files are being sent in the POST. If files are specified in the POST request, then the Content-Type
header will become ``multipart/form-data``.
The ``post()`` method of a client object accepts four arguments: the URL, optional headers, post fields, and an array of
request options. To send files in the POST request, prepend the ``@`` symbol to the array value (just like you would if
you were using the PHP ``curl_setopt`` function).
Here's how to create a multipart/form-data POST request containing files and fields:
.. code-block:: php
$request = $client->post('http://httpbin.org/post', array(), array(
'custom_field' => 'my custom value',
'file_field' => '@/path/to/file.xml'
));
$response = $request->send();
.. note::
Remember to **always** sanitize user input when sending POST requests:
.. code-block:: php
// Prevent users from accessing sensitive files by sanitizing input
$_POST = array('firstname' => '@/etc/passwd');
$request = $client->post('http://www.example.com', array(), array (
'firstname' => str_replace('@', '', $_POST['firstname'])
));
You can alternatively build up the contents of a POST request.
.. code-block:: php
$request = $client->post('http://httpbin.org/post')
->setPostField('custom_field', 'my custom value')
->addPostFile('file', '/path/to/file.xml');
$response = $request->send();
Raw POST data
^^^^^^^^^^^^^
POST requests can also contain raw POST data that is not related to HTML forms.
.. code-block:: php
$request = $client->post('http://httpbin.org/post', array(), 'this is the body');
$response = $request->send();
You can set the body of POST request using the ``setBody()`` method of the
``Guzzle\Http\Message\EntityEnclosingRequest`` object. This method accepts a string, a resource returned from
``fopen``, or a ``Guzzle\Http\EntityBodyInterface`` object.
.. code-block:: php
$request = $client->post('http://httpbin.org/post');
// Set the body of the POST to stream the contents of /path/to/large_body.txt
$request->setBody(fopen('/path/to/large_body.txt', 'r'));
$response = $request->send();
PUT requests
~~~~~~~~~~~~
The `PUT method <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6>`_ requests that the enclosed entity be
stored under the supplied Request-URI. PUT requests are similar to POST requests in that they both can send an entity
body in the request message.
The body of a PUT request (any any ``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object) is always stored as
a ``Guzzle\Http\Message\EntityBodyInterface`` object. This allows a great deal of flexibility when sending data to a
remote server. For example, you can stream the contents of a stream returned by fopen, stream the contents of a
callback function, or simply send a string of data.
.. code-block:: php
$request = $client->put('http://httpbin.org/put', array(), 'this is the body');
$response = $request->send();
Just like with POST, PATH, and DELETE requests, you can set the body of a PUT request using the ``setBody()`` method.
.. code-block:: php
$request = $client->put('http://httpbin.org/put');
$request->setBody(fopen('/path/to/large_body.txt', 'r'));
$response = $request->send();
PATCH requests
~~~~~~~~~~~~~~
`PATCH requests <http://tools.ietf.org/html/rfc5789>`_ are used to modify a resource.
.. code-block:: php
$request = $client->patch('http://httpbin.org', array(), 'this is the body');
$response = $request->send();
OPTIONS requests
~~~~~~~~~~~~~~~~
The `OPTIONS method <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2>`_ represents a request for
information about the communication options available on the request/response chain identified by the Request-URI.
.. code-block:: php
$request = $client->options('http://httpbin.org');
$response = $request->send();
// Check if the PUT method is supported by this resource
var_export($response->isMethodAllows('PUT'));
Custom requests
~~~~~~~~~~~~~~~
You can create custom HTTP requests that use non-standard HTTP methods using the ``createRequest()`` method of a
client object.
.. code-block:: php
$request = $client->createRequest('COPY', 'http://example.com/foo', array(
'Destination' => 'http://example.com/bar',
'Overwrite' => 'T'
));
$response = $request->send();
Query string parameters
-----------------------
Query string parameters of a request are owned by a request's ``Guzzle\Http\Query`` object that is accessible by
calling ``$request->getQuery()``. The Query class extends from ``Guzzle\Common\Collection`` and allows you to set one
or more query string parameters as key value pairs. You can set a parameter on a Query object using the
``set($key, $value)`` method or access the query string object like an associative array. Any previously specified
value for a key will be overwritten when using ``set()``. Use ``add($key, $value)`` to add a value to query string
object, and in the event of a collision with an existing value at a specific key, the value will be converted to an
array that contains all of the previously set values.
.. code-block:: php
$request = new Guzzle\Http\Message\Request('GET', 'http://www.example.com?foo=bar&abc=123');
$query = $request->getQuery();
echo "{$query}\n";
// >>> foo=bar&abc=123
$query->remove('abc');
echo "{$query}\n";
// >>> foo=bar
$query->set('foo', 'baz');
echo "{$query}\n";
// >>> foo=baz
$query->add('foo', 'bar');
echo "{$query}\n";
// >>> foo%5B0%5D=baz&foo%5B1%5D=bar
Whoah! What happened there? When ``foo=bar`` was added to the existing ``foo=baz`` query string parameter, the
aggregator associated with the Query object was used to help convert multi-value query string parameters into a string.
Let's disable URL-encoding to better see what's happening.
.. code-block:: php
$query->useUrlEncoding(false);
echo "{$query}\n";
// >>> foo[0]=baz&foo[1]=bar
.. note::
URL encoding can be disabled by passing false, enabled by passing true, set to use RFC 1738 by passing
``Query::FORM_URLENCODED`` (internally uses PHP's ``urlencode`` function), or set to RFC 3986 by passing
``Query::RFC_3986`` (this is the default and internally uses PHP's ``rawurlencode`` function).
As you can see, the multiple values were converted into query string parameters following the default PHP convention of
adding numerically indexed square bracket suffixes to each key (``foo[0]=baz&foo[1]=bar``). The strategy used to convert
multi-value parameters into a string can be customized using the ``setAggregator()`` method of the Query class. Guzzle
ships with the following query string aggregators by default:
1. ``Guzzle\Http\QueryAggregator\PhpAggregator``: Aggregates using PHP style brackets (e.g. ``foo[0]=baz&foo[1]=bar``)
2. ``Guzzle\Http\QueryAggregator\DuplicateAggregator``: Performs no aggregation and allows for key value pairs to be
repeated in a URL (e.g. ``foo=baz&foo=bar``)
3. ``Guzzle\Http\QueryAggregator\CommaAggregator``: Aggregates using commas (e.g. ``foo=baz,bar``)
.. _http-message-headers:
HTTP Message Headers
--------------------
HTTP message headers are case insensitive, multiple occurrences of any header can be present in an HTTP message
(whether it's valid or not), and some servers require specific casing of particular headers. Because of this, request
and response headers are stored in ``Guzzle\Http\Message\Header`` objects. The Header object can be cast as a string,
counted, or iterated to retrieve each value from the header. Casting a Header object to a string will return all of
the header values concatenated together using a glue string (typically ", ").
A request (and response) object have several methods that allow you to retrieve and modify headers.
* ``getHeaders()``: Get all of the headers of a message as a ``Guzzle\Http\Message\Header\HeaderCollection`` object.
* ``getHeader($header)``: Get a specific header from a message. If the header exists, you'll get a
``Guzzle\Http\Message\Header`` object. If the header does not exist, this methods returns ``null``.
* ``hasHeader($header)``: Returns true or false based on if the message has a particular header.
* ``setHeader($header, $value)``: Set a header value and overwrite any previously set value for this header.
* ``addHeader($header, $value)``: Add a header with a particular name. If a previous value was already set by the same,
then the header will contain multiple values.
* ``removeHeader($header)``: Remove a header by name from the message.
.. code-block:: php
$request = new Request('GET', 'http://httpbin.com/cookies');
// addHeader will set and append to any existing header values
$request->addHeader('Foo', 'bar');
$request->addHeader('foo', 'baz');
// setHeader overwrites any existing values
$request->setHeader('Test', '123');
// Request headers can be cast as a string
echo $request->getHeader('Foo');
// >>> bar, baz
echo $request->getHeader('Test');
// >>> 123
// You can count the number of headers of a particular case insensitive name
echo count($request->getHeader('foO'));
// >>> 2
// You can iterate over Header objects
foreach ($request->getHeader('foo') as $header) {
echo $header . "\n";
}
// You can get all of the request headers as a Guzzle\Http\Message\Header\HeaderCollection object
$headers = $request->getHeaders();
// Missing headers return NULL
var_export($request->getHeader('Missing'));
// >>> null
// You can see all of the different variations of a header by calling raw() on the Header
var_export($request->getHeader('foo')->raw());
Setting the body of a request
-----------------------------
Requests that can send a body (e.g. PUT, POST, DELETE, PATCH) are instances of
``Guzzle\Http\Message\EntityEnclosingRequestInterface``. Entity enclosing requests contain several methods that allow
you to specify the body to send with a request.
Use the ``setBody()`` method of a request to set the body that will be sent with a request. This method accepts a
string, a resource returned by ``fopen()``, an array, or an instance of ``Guzzle\Http\EntityBodyInterface``. The body
will then be streamed from the underlying ``EntityBodyInterface`` object owned by the request. When setting the body
of the request, you can optionally specify a Content-Type header and whether or not to force the request to use
chunked Transfer-Encoding.
.. code-block:: php
$request = $client->put('/user.json');
$request->setBody('{"foo":"baz"}', 'application/json');
Content-Type header
~~~~~~~~~~~~~~~~~~~
Guzzle will automatically add a Content-Type header to a request if the Content-Type can be guessed based on the file
extension of the payload being sent or the file extension present in the path of a request.
.. code-block:: php
$request = $client->put('/user.json', array(), '{"foo":"bar"}');
// The Content-Type was guessed based on the path of the request
echo $request->getHeader('Content-Type');
// >>> application/json
$request = $client->put('/user.json');
$request->setBody(fopen('/tmp/user_data.json', 'r'));
// The Content-Type was guessed based on the path of the entity body
echo $request->getHeader('Content-Type');
// >>> application/json
Transfer-Encoding: chunked header
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When sending HTTP requests that contain a payload, you must let the remote server know how to determine when the entire
message has been sent. This usually is done by supplying a ``Content-Length`` header that tells the origin server the
size of the body that is to be sent. In some cases, the size of the payload being sent in a request cannot be known
before initiating the transfer. In these cases (when using HTTP/1.1), you can use the ``Transfer-Encoding: chunked``
header.
If the Content-Length cannot be determined (i.e. using a PHP ``http://`` stream), then Guzzle will automatically add
the ``Transfer-Encoding: chunked`` header to the request.
.. code-block:: php
$request = $client->put('/user.json');
$request->setBody(fopen('http://httpbin.org/get', 'r'));
// The Content-Length could not be determined
echo $request->getHeader('Transfer-Encoding');
// >>> chunked
See :doc:`/http-client/entity-bodies` for more information on entity bodies.
Expect: 100-Continue header
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``Expect: 100-Continue`` header is used to help a client prevent sending a large payload to a server that will
reject the request. This allows clients to fail fast rather than waste bandwidth sending an erroneous payload. Guzzle
will automatically add the ``Expect: 100-Continue`` header to a request when the size of the payload exceeds 1MB or if
the body of the request is not seekable (this helps to prevent errors when a non-seekable body request is redirected).
.. note::
If you find that your larger requests are taking too long to complete, you should first check if the
``Expect: 100-Continue`` header is being sent with the request. Some servers do not respond well to this header,
which causes cURL to sleep for `1 second <http://curl.haxx.se/mail/lib-2010-01/0182.html>`_.
POST fields and files
~~~~~~~~~~~~~~~~~~~~~
Any entity enclosing request can send POST style fields and files. This includes POST, PUT, PATCH, and DELETE requests.
Any request that has set POST fields or files will use cURL's POST message functionality.
.. code-block:: php
$request = $client->post('/post');
// Set an overwrite any previously specified value
$request->setPostField('foo', 'bar');
// Append a value to any existing values
$request->getPostFields()->add('foo', 'baz');
// Remove a POST field by name
$request->removePostField('fizz');
// Add a file to upload (forces multipart/form-data)
$request->addPostFile('my_file', '/path/to/file', 'plain/text');
// Remove a POST file by POST key name
$request->removePostFile('my_other_file');
.. tip::
Adding a large number of POST fields to a POST request is faster if you use the ``addPostFields()`` method so that
you can add and process multiple fields with a single call. Adding multiple POST files is also faster using
``addPostFiles()``.
Working with cookies
--------------------
Cookies can be modified and retrieved from a request using the following methods:
.. code-block:: php
$request->addCookie($name, $value);
$request->removeCookie($name);
$value = $request->getCookie($name);
$valueArray = $request->getCookies();
Use the :doc:`cookie plugin </plugins/cookie-plugin>` if you need to reuse cookies between requests.
.. _request-set-response-body:
Changing where a response is downloaded
----------------------------------------
When a request is sent, the body of the response will be stored in a PHP temp stream by default. You can change the
location in which the response will be downloaded using ``$request->setResponseBody($body)`` or the ``save_to`` request
option. This can be useful for downloading the contents of a URL to a specific file.
Here's an example of using request options:
.. code-block:: php
$request = $this->client->get('http://example.com/large.mov', array(), array(
'save_to' => '/tmp/large_file.mov'
));
$request->send();
var_export(file_exists('/tmp/large_file.mov'));
// >>> true
Here's an example of using ``setResponseBody()``:
.. code-block:: php
$body = fopen('/tmp/large_file.mov', 'w');
$request = $this->client->get('http://example.com/large.mov');
$request->setResponseBody($body);
// You can more easily specify the name of a file to save the contents
// of the response to by passing a string to ``setResponseBody()``.
$request = $this->client->get('http://example.com/large.mov');
$request->setResponseBody('/tmp/large_file.mov');
Custom cURL options
-------------------
Most of the functionality implemented in the libcurl bindings has been simplified and abstracted by Guzzle. Developers
who need access to `cURL specific functionality <http://www.php.net/curl_setopt>`_ can still add cURL handle
specific behavior to Guzzle HTTP requests by modifying the cURL options collection of a request:
.. code-block:: php
$request->getCurlOptions()->set(CURLOPT_LOW_SPEED_LIMIT, 200);
Other special options that can be set in the ``curl.options`` array include:
+-------------------------+---------------------------------------------------------------------------------+
| debug | Adds verbose cURL output to a temp stream owned by the cURL handle object |
+-------------------------+---------------------------------------------------------------------------------+
| progress | Instructs cURL to emit events when IO events occur. This allows you to be |
| | notified when bytes are transferred over the wire by subscribing to a request's |
| | ``curl.callback.read``, ``curl.callback.write``, and ``curl.callback.progress`` |
| | events. |
+-------------------------+---------------------------------------------------------------------------------+
Request options
---------------
Requests options can be specified when creating a request or in the ``request.options`` parameter of a client. These
options can control various aspects of a request including: headers to send, query string data, where the response
should be downloaded, proxies, auth, etc.
.. code-block:: php
$request = $client->get($url, $headers, array('proxy' => 'http://proxy.com'));
See :ref:`Request options <request-options>` for more information.
Working with errors
-------------------
HTTP errors
~~~~~~~~~~~
Requests that receive a 4xx or 5xx response will throw a ``Guzzle\Http\Exception\BadResponseException``. More
specifically, 4xx errors throw a ``Guzzle\Http\Exception\ClientErrorResponseException``, and 5xx errors throw a
``Guzzle\Http\Exception\ServerErrorResponseException``. You can catch the specific exceptions or just catch the
BadResponseException to deal with either type of error. Here's an example of catching a generic BadResponseException:
.. code-block:: php
try {
$response = $client->get('/not_found.xml')->send();
} catch (Guzzle\Http\Exception\BadResponseException $e) {
echo 'Uh oh! ' . $e->getMessage();
echo 'HTTP request URL: ' . $e->getRequest()->getUrl() . "\n";
echo 'HTTP request: ' . $e->getRequest() . "\n";
echo 'HTTP response status: ' . $e->getResponse()->getStatusCode() . "\n";
echo 'HTTP response: ' . $e->getResponse() . "\n";
}
Throwing an exception when a 4xx or 5xx response is encountered is the default behavior of Guzzle requests. This
behavior can be overridden by adding an event listener with a higher priority than -255 that stops event propagation.
You can subscribe to ``request.error`` to receive notifications any time an unsuccessful response is received.
You can change the response that will be associated with the request by calling ``setResponse()`` on the
``$event['request']`` object passed into your listener, or by changing the ``$event['response']`` value of the
``Guzzle\Common\Event`` object that is passed to your listener. Transparently changing the response associated with a
request by modifying the event allows you to retry failed requests without complicating the code that uses the client.
This might be useful for sending requests to a web service that has expiring auth tokens. When a response shows that
your token has expired, you can get a new token, retry the request with the new token, and return the successful
response to the user.
Here's an example of retrying a request using updated authorization credentials when a 401 response is received,
overriding the response of the original request with the new response, and still allowing the default exception
behavior to be called when other non-200 response status codes are encountered:
.. code-block:: php
// Add custom error handling to any request created by this client
$client->getEventDispatcher()->addListener('request.error', function(Event $event) {
if ($event['response']->getStatusCode() == 401) {
$newRequest = $event['request']->clone();
$newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken());
$newResponse = $newRequest->send();
// Set the response object of the request without firing more events
$event['response'] = $newResponse;
// You can also change the response and fire the normal chain of
// events by calling $event['request']->setResponse($newResponse);
// Stop other events from firing when you override 401 responses
$event->stopPropagation();
}
});
cURL errors
~~~~~~~~~~~
Connection problems and cURL specific errors can also occur when transferring requests using Guzzle. When Guzzle
encounters cURL specific errors while transferring a single request, a ``Guzzle\Http\Exception\CurlException`` is
thrown with an informative error message and access to the cURL error message.
A ``Guzzle\Http\Exception\MultiTransferException`` exception is thrown when a cURL specific error occurs while
transferring multiple requests in parallel. You can then iterate over all of the exceptions encountered during the
transfer.
Plugins and events
------------------
Guzzle request objects expose various events that allow you to hook in custom logic. A request object owns a
``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling
``$request->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or
event subscribers (classes that listen to specific events of a dispatcher). You can add event subscribers to a request
directly by just calling ``$request->addSubscriber($mySubscriber);``.
.. _request-events:
Events emitted from a request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A ``Guzzle\Http\Message\Request`` and ``Guzzle\Http\Message\EntityEnclosingRequest`` object emit the following events:
+------------------------------+--------------------------------------------+------------------------------------------+
| Event name | Description | Event data |
+==============================+============================================+==========================================+
| request.before_send | About to send request | * request: Request to be sent |
+------------------------------+--------------------------------------------+------------------------------------------+
| request.sent | Sent the request | * request: Request that was sent |
| | | * response: Received response |
+------------------------------+--------------------------------------------+------------------------------------------+
| request.complete | Completed a full HTTP transaction | * request: Request that was sent |
| | | * response: Received response |
+------------------------------+--------------------------------------------+------------------------------------------+
| request.success | Completed a successful request | * request: Request that was sent |
| | | * response: Received response |
+------------------------------+--------------------------------------------+------------------------------------------+
| request.error | Completed an unsuccessful request | * request: Request that was sent |
| | | * response: Received response |
+------------------------------+--------------------------------------------+------------------------------------------+
| request.exception | An unsuccessful response was | * request: Request |
| | received. | * response: Received response |
| | | * exception: BadResponseException |
+------------------------------+--------------------------------------------+------------------------------------------+
| request.receive.status_line | Received the start of a response | * line: Full response start line |
| | | * status_code: Status code |
| | | * reason_phrase: Reason phrase |
| | | * previous_response: (e.g. redirect) |
+------------------------------+--------------------------------------------+------------------------------------------+
| curl.callback.progress | cURL progress event (only dispatched when | * handle: CurlHandle |
| | ``emit_io`` is set on a request's curl | * download_size: Total download size |
| | options) | * downloaded: Bytes downloaded |
| | | * upload_size: Total upload bytes |
| | | * uploaded: Bytes uploaded |
+------------------------------+--------------------------------------------+------------------------------------------+
| curl.callback.write | cURL event called when data is written to | * request: Request |
| | an outgoing stream | * write: Data being written |
+------------------------------+--------------------------------------------+------------------------------------------+
| curl.callback.read | cURL event called when data is written to | * request: Request |
| | an incoming stream | * read: Data being read |
+------------------------------+--------------------------------------------+------------------------------------------+
Creating a request event listener
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here's an example that listens to the ``request.complete`` event of a request and prints the request and response.
.. code-block:: php
use Guzzle\Common\Event;
$request = $client->get('http://www.google.com');
// Echo out the response that was received
$request->getEventDispatcher()->addListener('request.complete', function (Event $e) {
echo $e['request'] . "\n\n";
echo $e['response'];
});
---
### Crmeb Vendor Guzzle Guzzle Docs Http Client Response (`crmeb/vendor/guzzle/guzzle/docs/http-client/response.rst`)
======================
Using Response objects
======================
Sending a request will return a ``Guzzle\Http\Message\Response`` object. You can view the raw HTTP response message by
casting the Response object to a string. Casting the response to a string will return the entity body of the response
as a string too, so this might be an expensive operation if the entity body is stored in a file or network stream. If
you only want to see the response headers, you can call ``getRawHeaders()``.
Response status line
--------------------
The different parts of a response's `status line <http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1>`_
(the first line of the response HTTP message) are easily retrievable.
.. code-block:: php
$response = $client->get('http://www.amazon.com')->send();
echo $response->getStatusCode(); // >>> 200
echo $response->getReasonPhrase(); // >>> OK
echo $response->getProtocol(); // >>> HTTP
echo $response->getProtocolVersion(); // >>> 1.1
You can determine the type of the response using several helper methods:
.. code-block:: php
$response->isSuccessful(); // true
$response->isInformational();
$response->isRedirect();
$response->isClientError();
$response->isServerError();
Response headers
----------------
The Response object contains helper methods for retrieving common response headers. These helper methods normalize the
variations of HTTP response headers.
.. code-block:: php
$response->getCacheControl();
$response->getContentType();
$response->getContentLength();
$response->getContentEncoding();
$response->getContentMd5();
$response->getEtag();
// etc... There are methods for every known response header
You can interact with the Response headers using the same exact methods used to interact with Request headers. See
:ref:`http-message-headers` for more information.
.. code-block:: php
echo $response->getHeader('Content-Type');
echo $response->getHeader('Content-Length');
echo $response->getHeaders()['Content-Type']; // PHP 5.4
Response body
-------------
The entity body object of a response can be retrieved by calling ``$response->getBody()``. The response EntityBody can
be cast to a string, or you can pass ``true`` to this method to retrieve the body as a string.
.. code-block:: php
$request = $client->get('http://www.amazon.com');
$response = $request->send();
echo $response->getBody();
See :doc:`/http-client/entity-bodies` for more information on entity bodies.
JSON Responses
~~~~~~~~~~~~~~
You can easily parse and use a JSON response as an array using the ``json()`` method of a response. This method will
always return an array if the response is valid JSON or if the response body is empty. You will get an exception if you
call this method and the response is not valid JSON.
.. code-block:: php
$data = $response->json();
echo gettype($data);
// >>> array
XML Responses
~~~~~~~~~~~~~
You can easily parse and use a XML response as SimpleXMLElement object using the ``xml()`` method of a response. This
method will always return a SimpleXMLElement object if the response is valid XML or if the response body is empty. You
will get an exception if you call this method and the response is not valid XML.
.. code-block:: php
$xml = $response->xml();
echo $xml->foo;
// >>> Bar!
Streaming responses
-------------------
Some web services provide streaming APIs that allow a client to keep a HTTP request open for an extended period of
time while polling and reading. Guzzle provides a simple way to convert HTTP request messages into
``Guzzle\Stream\Stream`` objects so that you can send the initial headers of a request, read the response headers, and
pull in the response body manually as needed.
Here's an example using the Twitter Streaming API to track the keyword "bieber":
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Stream\PhpStreamRequestFactory;
$client = new Client('https://stream.twitter.com/1');
$request = $client->post('statuses/filter.json', null, array(
'track' => 'bieber'
));
$request->setAuth('myusername', 'mypassword');
$factory = new PhpStreamRequestFactory();
$stream = $factory->fromRequest($request);
// Read until the stream is closed
while (!$stream->feof()) {
// Read a line from the stream
$line = $stream->readLine();
// JSON decode the line of data
$data = json_decode($line, true);
}
You can use the ``stream`` request option when using a static client to more easily create a streaming response.
.. code-block:: php
$stream = Guzzle::get('http://guzzlephp.org', array('stream' => true));
while (!$stream->feof()) {
echo $stream->readLine();
}
---
### Crmeb Vendor Guzzle Guzzle Docs Http Client Uri Templates (`crmeb/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst`)
=============
URI templates
=============
The ``$uri`` passed to one of the client's request creational methods or the base URL of a client can utilize URI
templates. Guzzle supports the entire `URI templates RFC <http://tools.ietf.org/html/rfc6570>`_. URI templates add a
special syntax to URIs that replace template place holders with user defined variables.
Every request created by a Guzzle HTTP client passes through a URI template so that URI template expressions are
automatically expanded:
.. code-block:: php
$client = new Guzzle\Http\Client('https://example.com/', array('a' => 'hi'));
$request = $client->get('/{a}');
Because of URI template expansion, the URL of the above request will become ``https://example.com/hi``. Notice that
the template was expanded using configuration variables of the client. You can pass in custom URI template variables
by passing the URI of your request as an array where the first index of the array is the URI template and the second
index of the array are template variables that are merged into the client's configuration variables.
.. code-block:: php
$request = $client->get(array('/test{?a,b}', array('b' => 'there')));
The URL for this request will become ``https://test.com?a=hi&b=there``. URI templates aren't limited to just simple
variable replacements; URI templates can provide an enormous amount of flexibility when creating request URIs.
.. code-block:: php
$request = $client->get(array('http://example.com{+path}{/segments*}{?query,data*}', array(
'path' => '/foo/bar',
'segments' => array('one', 'two'),
'query' => 'test',
'data' => array(
'more' => 'value'
)
)));
The resulting URL would become ``http://example.com/foo/bar/one/two?query=test&more=value``.
By default, URI template expressions are enclosed in an opening and closing brace (e.g. ``{var}``). If you are working
with a web service that actually uses braces (e.g. Solr), then you can specify a custom regular expression to use to
match URI template expressions.
.. code-block:: php
$client->getUriTemplate()->setRegex('/\<\$(.+)\>/');
$client->get('/<$a>');
You can learn about all of the different features of URI templates by reading the
`URI templates RFC <http://tools.ietf.org/html/rfc6570>`_.
---
### Crmeb Vendor Guzzle Guzzle Docs Iterators Guzzle Iterators (`crmeb/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst`)
================
Guzzle iterators
================
Guzzle provides several SPL iterators that can be used with other SPL iterators, including Guzzle resource iterators.
Guzzle's ``guzzle/iterator`` component can also be used independently of the rest of Guzzle through Packagist and
Composer: https://packagist.org/packages/guzzle/iterator
ChunkedIterator
---------------
Pulls out multiple values from an inner iterator and yields and array of values for each outer iteration -- essentially
pulling out chunks of values from the inner iterator.
.. code-block:: php
use Guzzle\Iterator\ChunkedIterator;
$inner = new ArrayIterator(range(0, 8));
$chunkedIterator = new ChunkedIterator($inner, 2);
foreach ($chunkedIterator as $chunk) {
echo implode(', ', $chunk) . "\n";
}
// >>> 0, 1
// >>> 2, 3
// >>> 4, 5
// >>> 6, 7
// >>> 8
FilterIterator
--------------
This iterator is used to filter values out of the inner iterator. This iterator can be used when PHP 5.4's
CallbackFilterIterator is not available.
.. code-block:: php
use Guzzle\Iterator\FilterIterator;
$inner = new ArrayIterator(range(1, 10));
$filterIterator = new FilterIterator($inner, function ($value) {
return $value % 2;
});
foreach ($filterIterator as $value) {
echo $value . "\n";
}
// >>> 2
// >>> 4
// >>> 6
// >>> 8
// >>> 10
MapIterator
-----------
This iterator modifies the values of the inner iterator before yielding.
.. code-block:: php
use Guzzle\Iterator\MapIterator;
$inner = new ArrayIterator(range(0, 3));
$mapIterator = new MapIterator($inner, function ($value) {
return $value * 10;
});
foreach ($mapIterator as $value) {
echo $value . "\n";
}
// >>> 0
// >>> 10
// >>> 20
// >>> 30
MethodProxyIterator
-------------------
This decorator is useful when you need to expose a specific method from an inner iterator that might be wrapper
by one or more iterator decorators. This decorator proxies missing method calls to each inner iterator until one
of the inner iterators can fulfill the call.
.. code-block:: php
use Guzzle\Iterator\MethodProxyIterator;
$inner = new \ArrayIterator();
$proxy = new MethodProxyIterator($inner);
// Proxy method calls to the ArrayIterator
$proxy->append('a');
$proxy->append('b');
---
### Crmeb Vendor Guzzle Guzzle Docs Iterators Resource Iterators (`crmeb/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst`)
==================
Resource iterators
==================
Web services often implement pagination in their responses which requires the end-user to issue a series of consecutive
requests in order to fetch all of the data they asked for. Users of your web service client should not be responsible
for implementing the logic involved in iterating through pages of results. Guzzle provides a simple resource iterator
foundation to make it easier on web service client developers to offer a useful abstraction layer.
Getting an iterator from a client
---------------------------------
ResourceIteratorInterface Guzzle\Service\Client::getIterator($command [, array $commandOptions, array $iteratorOptions ])
The ``getIterator`` method of a ``Guzzle\Service\ClientInterface`` object provides a convenient interface for
instantiating a resource iterator for a specific command. This method implicitly uses a
``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object to create resource iterators. Pass an
instantiated command object or the name of a command in the first argument. When passing the name of a command, the
command factory of the client will create the command by name using the ``$commandOptions`` array. The third argument
may be used to pass an array of options to the constructor of the instantiated ``ResourceIteratorInterface`` object.
.. code-block:: php
$iterator = $client->getIterator('get_users');
foreach ($iterator as $user) {
echo $user['name'] . ' age ' . $user['age'] . PHP_EOL;
}
The above code sample might execute a single request or a thousand requests. As a consumer of a web service, I don't
care. I just want to iterate over all of the users.
Iterator options
~~~~~~~~~~~~~~~~
The two universal options that iterators should support are ``limit`` and ``page_size``. Using the ``limit`` option
tells the resource iterator to attempt to limit the total number of iterated resources to a specific amount. Keep in
mind that this is not always possible due to limitations that may be inherent to a web service. The ``page_size``
option is used to tell a resource iterator how many resources to request per page of results. Much like the ``limit``
option, you can not rely on getting back exactly the number of resources your specify in the ``page_size`` option.
.. note::
The ``limit`` and ``page_size`` options can also be specified on an iterator using the ``setLimit($limit)`` and
``setPageSize($pageSize)`` methods.
Resolving iterator class names
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The default resource iterator factory of a client object expects that your iterators are stored under the ``Model``
folder of your client and that an iterator is names after the CamelCase name of a command followed by the word
"Iterator". For example, if you wanted to create an iterator for the ``get_users`` command, then your iterator class
would be ``Model\GetUsersIterator`` and would be stored in ``Model/GetUsersIterator.php``.
Creating an iterator
--------------------
While not required, resource iterators in Guzzle typically iterate using a ``Guzzle\Service\Command\CommandInterface``
object. ``Guzzle\Service\Resource\ResourceIterator``, the default iterator implementation that you should extend,
accepts a command object and array of iterator options in its constructor. The command object passed to the resource
iterator is expected to be ready to execute and not previously executed. The resource iterator keeps a reference of
this command and clones the original command each time a subsequent request needs to be made to fetch more data.
Implement the sendRequest method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The most important thing (and usually the only thing) you need to do when creating a resource iterator is to implement
the ``sendRequest()`` method of the resource iterator. The ``sendRequest()`` method is called when you begin
iterating or if there are no resources left to iterate and it you expect to retrieve more resources by making a
subsequent request. The ``$this->command`` property of the resource iterator is updated with a cloned copy of the
original command object passed into the constructor of the iterator. Use this command object to issue your subsequent
requests.
The ``sendRequest()`` method must return an array of the resources you retrieved from making the subsequent call.
Returning an empty array will stop the iteration. If you suspect that your web service client will occasionally return
an empty result set but still requires further iteration, then you must implement a sort of loop in your
``sendRequest()`` method that will continue to issue subsequent requests until your reach the end of the paginated
result set or until additional resources are retrieved from the web service.
Update the nextToken property
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Beyond fetching more results, the ``sendRequest()`` method is responsible for updating the ``$this->nextToken``
property of the iterator. Setting this property to anything other than null tells the iterator that issuing a
subsequent request using the nextToken value will probably return more results. You must continually update this
value in your ``sendRequest()`` method as each response is received from the web service.
Example iterator
----------------
Let's say you want to implement a resource iterator for the ``get_users`` command of your web service. The
``get_users`` command receives a response that contains a list of users, and if there are more pages of results to
retrieve, returns a value called ``next_user``. This return value is known as the **next token** and should be used to
issue subsequent requests.
Assume the response to a ``get_users`` command returns JSON data that looks like this:
.. code-block:: javascript
{
"users": [
{ "name": "Craig Johnson", "age": 10 },
{ "name": "Tom Barker", "age": 20 },
{ "name": "Bob Mitchell", "age": 74 }
],
"next_user": "Michael Dowling"
}
Assume that because there is a ``next_user`` value, there will be more users if a subsequent request is issued. If the
``next_user`` value is missing or null, then we know there are no more results to fetch. Let's implement a resource
iterator for this command.
.. code-block:: php
namespace MyService\Model;
use Guzzle\Service\Resource\ResourceIterator;
/**
* Iterate over a get_users command
*/
class GetUsersIterator extends ResourceIterator
{
protected function sendRequest()
{
// If a next token is set, then add it to the command
if ($this->nextToken) {
$this->command->set('next_user', $this->nextToken);
}
// Execute the command and parse the result
$result = $this->command->execute();
// Parse the next token
$this->nextToken = isset($result['next_user']) ? $result['next_user'] : false;
return $result['users'];
}
}
As you can see, it's pretty simple to implement an iterator. There are a few things that you should notice from this
example:
1. You do not need to create a new command in the ``sendRequest()`` method. A new command object is cloned from the
original command passed into the constructor of the iterator before the ``sendRequest()`` method is called.
Remember that the resource iterator expects a command that has not been executed.
2. When the ``sendRequest()`` method is first called, you will not have a ``$this->nextToken`` value, so always check
before setting it on a command. Notice that the next token is being updated each time a request is sent.
3. After fetching more resources from the service, always return an array of resources.
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Async Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst`)
============
Async plugin
============
The AsyncPlugin allows you to send requests that do not wait on a response. This is handled through cURL by utilizing
the progress event. When a request has sent all of its data to the remote server, Guzzle adds a 1ms timeout on the
request and instructs cURL to not download the body of the response. The async plugin then catches the exception and
adds a mock response to the request, along with an X-Guzzle-Async header to let you know that the response was not
fully downloaded.
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Plugin\Async\AsyncPlugin;
$client = new Client('http://www.example.com');
$client->addSubscriber(new AsyncPlugin());
$response = $client->get()->send();
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Backoff Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst`)
====================
Backoff retry plugin
====================
The ``Guzzle\Plugin\Backoff\BackoffPlugin`` automatically retries failed HTTP requests using custom backoff strategies:
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Plugin\Backoff\BackoffPlugin;
$client = new Client('http://www.test.com/');
// Use a static factory method to get a backoff plugin using the exponential backoff strategy
$backoffPlugin = BackoffPlugin::getExponentialBackoff();
// Add the backoff plugin to the client object
$client->addSubscriber($backoffPlugin);
The BackoffPlugin's constructor accepts a ``Guzzle\Plugin\Backoff\BackoffStrategyInterface`` object that is used to
determine when a retry should be issued and how long to delay between retries. The above code example shows how to
attach a BackoffPlugin to a client that is pre-configured to retry failed 500 and 503 responses using truncated
exponential backoff (emulating the behavior of Guzzle 2's ExponentialBackoffPlugin).
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Cache Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst`)
=================
HTTP Cache plugin
=================
Guzzle can leverage HTTP's caching specifications using the ``Guzzle\Plugin\Cache\CachePlugin``. The CachePlugin
provides a private transparent proxy cache that caches HTTP responses. The caching logic, based on
`RFC 2616 <http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html>`_, uses HTTP headers to control caching behavior,
cache lifetime, and supports Vary, ETag, and Last-Modified based revalidation:
.. code-block:: php
use Guzzle\Http\Client;
use Doctrine\Common\Cache\FilesystemCache;
use Guzzle\Cache\DoctrineCacheAdapter;
use Guzzle\Plugin\Cache\CachePlugin;
use Guzzle\Plugin\Cache\DefaultCacheStorage;
$client = new Client('http://www.test.com/');
$cachePlugin = new CachePlugin(array(
'storage' => new DefaultCacheStorage(
new DoctrineCacheAdapter(
new FilesystemCache('/path/to/cache/files')
)
)
));
// Add the cache plugin to the client object
$client->addSubscriber($cachePlugin);
$client->get('http://www.wikipedia.org/')->send();
// The next request will revalidate against the origin server to see if it
// has been modified. If a 304 response is received the response will be
// served from cache
$client->get('http://www.wikipedia.org/')->send();
The cache plugin intercepts GET and HEAD requests before they are actually transferred to the origin server. The cache
plugin then generates a hash key based on the request method and URL, and checks to see if a response exists in the cache. If
a response exists in the cache, the cache adapter then checks to make sure that the caching rules associated with the response
satisfy the request, and ensures that response still fresh. If the response is acceptable for the request any required
revalidation, then the cached response is served instead of contacting the origin server.
Vary
----
Cache keys are derived from a request method and a request URL. Multiple responses can map to the same cache key and
stored in Guzzle's underlying cache storage object. You should use the ``Vary`` HTTP header to tell the cache storage
object that the cache response must have been cached for a request that matches the headers specified in the Vary header
of the request. This allows you to have specific cache entries for the same request URL but variations in a request's
headers determine which cache entry is served. Please see the http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
for more information.
Cache options
-------------
There are several options you can add to requests or clients to modify the behavior of the cache plugin.
Override cache TTL
~~~~~~~~~~~~~~~~~~
You can override the number of seconds a cacheable response is stored in the cache by setting the
``cache.override_ttl`` parameter on the params object of a request:
.. code-block:: php
// If the response to the request is cacheable, then the response will be cached for 100 seconds
$request->getParams()->set('cache.override_ttl', 100);
If a response doesn't specify any freshness policy, it will be kept in cache for 3600 seconds by default.
Custom caching decision
~~~~~~~~~~~~~~~~~~~~~~~
If the service you are interacting with does not return caching headers or returns responses that are normally
something that would not be cached, you can set a custom ``can_cache`` object on the constructor of the CachePlugin
and provide a ``Guzzle\Plugin\Cache\CanCacheInterface`` object. You can use the
``Guzzle\Plugin\Cache\CallbackCanCacheStrategy`` to easily make a caching decision based on an HTTP request and
response.
Revalidation options
~~~~~~~~~~~~~~~~~~~~
You can change the revalidation behavior of a request using the ``cache.revalidate`` parameter. Setting this
parameter to ``never`` will ensure that a revalidation request is never sent, and the response is always served from
the origin server. Setting this parameter to ``skip`` will never revalidate and uses the response stored in the cache.
Normalizing requests for caching
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use the ``cache.key_filter`` parameter if you wish to strip certain query string parameters from your
request before creating a unique hash for the request. This parameter can be useful if your requests have query
string values that cause each request URL to be unique (thus preventing a cache hit). The ``cache.key_filter``
format is simply a comma separated list of query string values to remove from the URL when creating a cache key.
For example, here we are saying that the ``a`` and ``q`` query string variables should be ignored when generating a
cache key for the request:
.. code-block:: php
$request->getParams()->set('cache.key_filter', 'a, q');
Other options
~~~~~~~~~~~~~
There are many other options available to the CachePlugin that can meet almost any caching requirement, including
custom revalidation implementations, custom cache key generators, custom caching decision strategies, and custom
cache storage objects. Take a look the constructor of ``Guzzle\Plugin\Cache\CachePlugin`` for more information.
Setting Client-wide cache settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can specify cache settings for every request created by a client by adding cache settings to the configuration
options of a client.
.. code-block:: php
$client = new Guzzle\Http\Client('http://www.test.com', array(
'request.params' => array(
'cache.override_ttl' => 3600,
'params.cache.revalidate' => 'never'
)
));
echo $client->get('/')->getParams()->get('cache.override_ttl');
// >>> 3600
echo $client->get('/')->getParams()->get('cache.revalidate');
// >>> never
Cache revalidation
------------------
If the cache plugin determines that a response to a GET request needs revalidation, a conditional GET is transferred
to the origin server. If the origin server returns a 304 response, then a response containing the merged headers of
the cached response with the new response and the entity body of the cached response is returned. Custom revalidation
strategies can be injected into a CachePlugin if needed.
Cache adapters
--------------
Guzzle doesn't try to reinvent the wheel when it comes to caching or logging. Plenty of other frameworks have
excellent solutions in place that you are probably already using in your applications. Guzzle uses adapters for
caching and logging. The cache plugin requires a cache adapter so that is can store responses in a cache. Guzzle
currently supports cache adapters for `Doctrine 2.0 <http://www.doctrine-project.org/>`_ and the
`Zend Framework <http://framework.zend.com>`_.
Doctrine cache adapter
~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: php
use Doctrine\Common\Cache\ArrayCache;
use Guzzle\Cache\DoctrineCacheAdapter;
use Guzzle\Plugin\Cache\CachePlugin;
$backend = new ArrayCache();
$adapter = new DoctrineCacheAdapter($backend);
$cache = new CachePlugin($adapter);
Zend Framework cache adapter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: php
use Guzzle\Cache\ZendCacheAdapter;
use Zend\Cache\Backend\TestBackend;
$backend = new TestBackend();
$adapter = new ZendCacheAdapter($backend);
$cache = new CachePlugin($adapter);
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Cookie Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst`)
=============
Cookie plugin
=============
Some web services require a Cookie in order to maintain a session. The ``Guzzle\Plugin\Cookie\CookiePlugin`` will add
cookies to requests and parse cookies from responses using a CookieJar object:
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Plugin\Cookie\CookiePlugin;
use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
$cookiePlugin = new CookiePlugin(new ArrayCookieJar());
// Add the cookie plugin to a client
$client = new Client('http://www.test.com/');
$client->addSubscriber($cookiePlugin);
// Send the request with no cookies and parse the returned cookies
$client->get('http://www.yahoo.com/')->send();
// Send the request again, noticing that cookies are being sent
$request = $client->get('http://www.yahoo.com/');
$request->send();
echo $request;
You can disable cookies per-request by setting the ``cookies.disable`` value to true on a request's params object.
.. code-block:: php
$request->getParams()->set('cookies.disable', true);
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Creating Plugins (`crmeb/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst`)
================
Creating plugins
================
.. highlight:: php
Guzzle is extremely extensible because of the behavioral modifications that can be added to requests, clients, and
commands using an event system. Before and after the majority of actions are taken in the library, an event is emitted
with the name of the event and context surrounding the event. Observers can subscribe to a subject and modify the
subject based on the events received. Guzzle's event system utilizes the Symfony2 EventDispatcher and is the backbone
of its plugin architecture.
Overview
--------
Plugins must implement the ``Symfony\Component\EventDispatcher\EventSubscriberInterface`` interface. The
``EventSubscriberInterface`` requires that your class implements a static method, ``getSubscribedEvents()``, that
returns an associative array mapping events to methods on the object. See the
`Symfony2 documentation <http://symfony.com/doc/2.0/book/internals.html#the-event-dispatcher>`_ for more information.
Plugins can be attached to any subject, or object in Guzzle that implements that
``Guzzle\Common\HasDispatcherInterface``.
Subscribing to a subject
~~~~~~~~~~~~~~~~~~~~~~~~
You can subscribe an instantiated observer to an event by calling ``addSubscriber`` on a subject.
.. code-block:: php
$testPlugin = new TestPlugin();
$client->addSubscriber($testPlugin);
You can also subscribe to only specific events using a closure::
$client->getEventDispatcher()->addListener('request.create', function(Event $event) {
echo $event->getName();
echo $event['request'];
});
``Guzzle\Common\Event`` objects are passed to notified functions. The Event object has a ``getName()`` method which
return the name of the emitted event and may contain contextual information that can be accessed like an array.
Knowing what events to listen to
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Any class that implements the ``Guzzle\Common\HasDispatcherInterface`` must implement a static method,
``getAllEvents()``, that returns an array of the events that are emitted from the object. You can browse the source
to see each event, or you can call the static method directly in your code to get a list of available events.
Event hooks
-----------
* :ref:`client-events`
* :ref:`service-client-events`
* :ref:`request-events`
* ``Guzzle\Http\Curl\CurlMulti``:
* :ref:`service-builder-events`
Examples of the event system
----------------------------
Simple Echo plugin
~~~~~~~~~~~~~~~~~~
This simple plugin prints a string containing the request that is about to be sent by listening to the
``request.before_send`` event::
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class EchoPlugin implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array('request.before_send' => 'onBeforeSend');
}
public function onBeforeSend(Guzzle\Common\Event $event)
{
echo 'About to send a request: ' . $event['request'] . "\n";
}
}
$client = new Guzzle\Service\Client('http://www.test.com/');
// Create the plugin and add it as an event subscriber
$plugin = new EchoPlugin();
$client->addSubscriber($plugin);
// Send a request and notice that the request is printed to the screen
$client->get('/')->send();
Running the above code will print a string containing the HTTP request that is about to be sent.
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Curl Auth Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst`)
==========================
cURL authentication plugin
==========================
.. warning::
The CurlAuthPlugin is deprecated. You should use the `auth` parameter of a client to add authorization headers to
every request created by a client.
.. code-block:: php
$client->setDefaultOption('auth', array('username', 'password', 'Basic|Digest|NTLM|Any'));
If your web service client requires basic authorization, then you can use the CurlAuthPlugin to easily add an
Authorization header to each request sent by the client.
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Plugin\CurlAuth\CurlAuthPlugin;
$client = new Client('http://www.test.com/');
// Add the auth plugin to the client object
$authPlugin = new CurlAuthPlugin('username', 'password');
$client->addSubscriber($authPlugin);
$response = $client->get('projects/1/people')->send();
$xml = new SimpleXMLElement($response->getBody(true));
foreach ($xml->person as $person) {
echo $person->email . "\n";
}
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins History Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst`)
==============
History plugin
==============
The history plugin tracks all of the requests and responses sent through a request or client. This plugin can be
useful for crawling or unit testing. By default, the history plugin stores up to 10 requests and responses.
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Plugin\History\HistoryPlugin;
$client = new Client('http://www.test.com/');
// Add the history plugin to the client object
$history = new HistoryPlugin();
$history->setLimit(5);
$client->addSubscriber($history);
$client->get('http://www.yahoo.com/')->send();
echo $history->getLastRequest();
echo $history->getLastResponse();
echo count($history);
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Log Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst`)
==========
Log plugin
==========
Use the ``Guzzle\Plugin\Log\LogPlugin`` to view all data sent over the wire, including entity bodies and redirects.
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Log\Zf1LogAdapter;
use Guzzle\Plugin\Log\LogPlugin;
use Guzzle\Log\MessageFormatter;
$client = new Client('http://www.test.com/');
$adapter = new Zf1LogAdapter(
new \Zend_Log(new \Zend_Log_Writer_Stream('php://output'))
);
$logPlugin = new LogPlugin($adapter, MessageFormatter::DEBUG_FORMAT);
// Attach the plugin to the client, which will in turn be attached to all
// requests generated by the client
$client->addSubscriber($logPlugin);
$response = $client->get('http://google.com')->send();
The code sample above wraps a ``Zend_Log`` object using a ``Guzzle\Log\Zf1LogAdapter``. After attaching the plugin to
the client, all data sent over the wire will be logged to stdout.
The first argument of the LogPlugin's constructor accepts a ``Guzzle\Log\LogAdapterInterface`` object. This object is
an adapter that allows you to use the logging capabilities of your favorite log implementation. The second argument of
the constructor accepts a ``Guzzle\Log\MessageFormatter`` or a log messaged format string. The format string uses
variable substitution and allows you to define the log data that is important to your application. The different
variables that can be injected are as follows:
================== ====================================================================================
Variable Substitution
================== ====================================================================================
{request} Full HTTP request message
{response} Full HTTP response message
{ts} Timestamp
{host} Host of the request
{method} Method of the request
{url} URL of the request
{host} Host of the request
{protocol} Request protocol
{version} Protocol version
{resource} Resource of the request (path + query + fragment)
{port} Port of the request
{hostname} Hostname of the machine that sent the request
{code} Status code of the response (if available)
{phrase} Reason phrase of the response (if available)
{curl_error} Curl error message (if available)
{curl_code} Curl error code (if available)
{curl_stderr} Curl standard error (if available)
{connect_time} Time in seconds it took to establish the connection (if available)
{total_time} Total transaction time in seconds for last transfer (if available)
{req_header_*} Replace `*` with the lowercased name of a request header to add to the message
{res_header_*} Replace `*` with the lowercased name of a response header to add to the message
{req_body} Request body
{res_body} Response body
================== ====================================================================================
The LogPlugin has a helper method that can be used when debugging that will output the full HTTP request and
response of a transaction:
.. code-block:: php
$client->addSubscriber(LogPlugin::getDebugPlugin());
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Md5 Validator Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst`)
====================
MD5 validator plugin
====================
Entity bodies can sometimes be modified over the wire due to a faulty TCP transport or misbehaving proxy. If an HTTP
response contains a Content-MD5 header, then a MD5 hash of the entity body of a response can be compared against the
Content-MD5 header of the response to determine if the response was delivered intact. The
``Guzzle\Plugin\Md5\Md5ValidatorPlugin`` will throw an ``UnexpectedValueException`` if the calculated MD5 hash does
not match the Content-MD5 header value:
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Plugin\Md5\Md5ValidatorPlugin;
$client = new Client('http://www.test.com/');
$md5Plugin = new Md5ValidatorPlugin();
// Add the md5 plugin to the client object
$client->addSubscriber($md5Plugin);
$request = $client->get('http://www.yahoo.com/');
$request->send();
Calculating the MD5 hash of a large entity body or an entity body that was transferred using a Content-Encoding is an
expensive operation. When working in high performance applications, you might consider skipping the MD5 hash
validation for entity bodies bigger than a certain size or Content-Encoded entity bodies
(see ``Guzzle\Plugin\Md5\Md5ValidatorPlugin`` for more information).
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Mock Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst`)
===========
Mock plugin
===========
The mock plugin is useful for testing Guzzle clients. The mock plugin allows you to queue an array of responses that
will satisfy requests sent from a client by consuming the request queue in FIFO order.
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Plugin\Mock\MockPlugin;
use Guzzle\Http\Message\Response;
$client = new Client('http://www.test.com/');
$mock = new MockPlugin();
$mock->addResponse(new Response(200))
->addResponse(new Response(404));
// Add the mock plugin to the client object
$client->addSubscriber($mock);
// The following request will receive a 200 response from the plugin
$client->get('http://www.example.com/')->send();
// The following request will receive a 404 response from the plugin
$client->get('http://www.test.com/')->send();
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Oauth Plugin (`crmeb/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst`)
============
OAuth plugin
============
Guzzle ships with an OAuth 1.0 plugin that can sign requests using a consumer key, consumer secret, OAuth token,
and OAuth secret. Here's an example showing how to send an authenticated request to the Twitter REST API:
.. code-block:: php
use Guzzle\Http\Client;
use Guzzle\Plugin\Oauth\OauthPlugin;
$client = new Client('http://api.twitter.com/1');
$oauth = new OauthPlugin(array(
'consumer_key' => 'my_key',
'consumer_secret' => 'my_secret',
'token' => 'my_token',
'token_secret' => 'my_token_secret'
));
$client->addSubscriber($oauth);
$response = $client->get('statuses/public_timeline.json')->send();
If you need to use a custom signing method, you can pass a ``signature_method`` configuration option in the
constructor of the OAuth plugin. The ``signature_method`` option must be a callable variable that accepts a string to
sign and signing key and returns a signed string.
.. note::
You can omit the ``token`` and ``token_secret`` options to use two-legged OAuth.
---
### Crmeb Vendor Guzzle Guzzle Docs Plugins Plugins Overview (`crmeb/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst`)
======================
Plugin system overview
======================
The workflow of sending a request and parsing a response is driven by Guzzle's event system, which is powered by the
`Symfony2 Event Dispatcher component <http://symfony.com/doc/current/components/event_dispatcher/introduction.html>`_.
Any object in Guzzle that emits events will implement the ``Guzzle\Common\HasEventDispatcher`` interface. You can add
event subscribers directly to these objects using the ``addSubscriber()`` method, or you can grab the
``Symfony\Component\EventDispatcher\EventDispatcher`` object owned by the object using ``getEventDispatcher()`` and
add a listener or event subscriber.
Adding event subscribers to clients
-----------------------------------
Any event subscriber or event listener attached to the EventDispatcher of a ``Guzzle\Http\Client`` or
``Guzzle\Service\Client`` object will automatically be attached to all request objects created by the client. This
allows you to attach, for example, a HistoryPlugin to a client object, and from that point on, every request sent
through that client will utilize the HistoryPlugin.
.. code-block:: php
use Guzzle\Plugin\History\HistoryPlugin;
use Guzzle\Service\Client;
$client = new Client();
// Create a history plugin and attach it to the client
$history = new HistoryPlugin();
$client->addSubscriber($history);
// Create and send a request. This request will also utilize the HistoryPlugin
$client->get('http://httpbin.org')->send();
// Echo out the last sent request by the client
echo $history->getLastRequest();
.. tip::
:doc:`Create event subscribers <creating-plugins>`, or *plugins*, to implement reusable logic that can be
shared across clients. Event subscribers are also easier to test than anonymous functions.
Pre-Built plugins
-----------------
Guzzle provides easy to use request plugins that add behavior to requests based on signal slot event notifications
powered by the Symfony2 Event Dispatcher component.
* :doc:`async-plugin`
* :doc:`backoff-plugin`
* :doc:`cache-plugin`
* :doc:`cookie-plugin`
* :doc:`curl-auth-plugin`
* :doc:`history-plugin`
* :doc:`log-plugin`
* :doc:`md5-validator-plugin`
* :doc:`mock-plugin`
* :doc:`oauth-plugin`
---
### Crmeb Vendor Guzzle Guzzle Docs Testing Unit Testing (`crmeb/vendor/guzzle/guzzle/docs/testing/unit-testing.rst`)
===========================
Unit Testing Guzzle clients
===========================
Guzzle provides several tools that will enable you to easily unit test your web service clients.
* PHPUnit integration
* Mock responses
* node.js web server for integration testing
PHPUnit integration
-------------------
Guzzle is unit tested using `PHPUnit <http://www.phpunit.de/>`_. Your web service client's unit tests should extend
``Guzzle\Tests\GuzzleTestCase`` so that you can take advantage of some of the built in helpers.
In order to unit test your client, a developer would need to copy phpunit.xml.dist to phpunit.xml and make any needed
modifications. As a best practice and security measure for you and your contributors, it is recommended to add an
ignore statement to your SCM so that phpunit.xml is ignored.
Bootstrapping
~~~~~~~~~~~~~
Your web service client should have a tests/ folder that contains a bootstrap.php file. The bootstrap.php file
responsible for autoloading and configuring a ``Guzzle\Service\Builder\ServiceBuilder`` that is used throughout your
unit tests for loading a configured client. You can add custom parameters to your phpunit.xml file that expects users
to provide the path to their configuration data.
.. code-block:: php
Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Aws\Common\Aws::factory($_SERVER['CONFIG']));
Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Guzzle\Service\Builder\ServiceBuilder::factory(array(
'test.unfuddle' => array(
'class' => 'Guzzle.Unfuddle.UnfuddleClient',
'params' => array(
'username' => 'test_user',
'password' => '****',
'subdomain' => 'test'
)
)
)));
The above code registers a service builder that can be used throughout your unit tests. You would then be able to
retrieve an instantiated and configured Unfuddle client by calling ``$this->getServiceBuilder()->get('test.unfuddle)``.
The above code assumes that ``$_SERVER['CONFIG']`` contains the path to a file that stores service description
configuration.
Unit testing remote APIs
------------------------
Mock responses
~~~~~~~~~~~~~~
One of the benefits of unit testing is the ability to quickly determine if there are errors in your code. If your
unit tests run slowly, then they become tedious and will likely be run less frequently. Guzzle's philosophy on unit
testing web service clients is that no network access should be required to run the unit tests. This means that
responses are served from mock responses or local servers. By adhering to this principle, tests will run much faster
and will not require an external resource to be available. The problem with this approach is that your mock responses
must first be gathered and then subsequently updated each time the remote API changes.
Integration testing over the internet
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can perform integration testing with a web service over the internet by making calls directly to the service. If
the web service you are requesting uses a complex signing algorithm or some other specific implementation, then you
may want to include at least one actual network test that can be run specifically through the command line using
`PHPUnit group annotations <http://www.phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.group>`_.
@group internet annotation
^^^^^^^^^^^^^^^^^^^^^^^^^^
When creating tests that require an internet connection, it is recommended that you add ``@group internet`` annotations
to your unit tests to specify which tests require network connectivity.
You can then `run PHPUnit tests <http://www.phpunit.de/manual/current/en/textui.html>`_ that exclude the @internet
group by running ``phpunit --exclude-group internet``.
API credentials
^^^^^^^^^^^^^^^
If API credentials are required to run your integration tests, you must add ``<php>`` parameters to your
phpunit.xml.dist file and extract these parameters in your bootstrap.php file.
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./tests/bootstrap.php" colors="true">
<php>
<server name="CONFIG" value="test_services.json" />
<server name="API_USER" value="change_me" />
<server name="API_PASSWORD" value="****" />
</php>
<testsuites>
<testsuite name="guzzle-service">
<directory suffix="Test.php">./Tests</directory>
</testsuite>
</testsuites>
</phpunit>
You can then extract the ``server`` variables in your bootstrap.php file by grabbing them from the ``$_SERVER``
superglobal: ``$apiUser = $_SERVER['API_USER'];``
Further reading
^^^^^^^^^^^^^^^
A good discussion on the topic of testing remote APIs can be found in Sebastian Bergmann's
`Real-World Solutions for Developing High-Quality PHP Frameworks and Applications <http://www.amazon.com/dp/0470872497>`_.
Queueing Mock responses
-----------------------
Mock responses can be used to test if requests are being generated correctly and responses and handled correctly by
your client. Mock responses can be queued up for a client using the ``$this->setMockResponse($client, $path)`` method
of your test class. Pass the client you are adding mock responses to and a single path or array of paths to mock
response files relative to the ``/tests/mock/ folder``. This will queue one or more mock responses for your client by
creating a simple observer on the client. Mock response files must contain a full HTTP response message:
.. code-block:: none
HTTP/1.1 200 OK
Date: Wed, 25 Nov 2009 12:00:00 GMT
Connection: close
Server: AmazonS3
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">EU</LocationConstraint>
After queuing mock responses for a client, you can get an array of the requests that were sent by the client that
were issued a mock response by calling ``$this->getMockedRequests()``.
You can also use the ``Guzzle\Plugin\Mock\MockPlugin`` object directly with your clients.
.. code-block:: php
$plugin = new Guzzle\Plugin\Mock\MockPlugin();
$plugin->addResponse(new Guzzle\Http\Message\Response(200));
$client = new Guzzle\Http\Client();
$client->addSubscriber($plugin);
// The following request will get the mock response from the plugin in FIFO order
$request = $client->get('http://www.test.com/');
$request->send();
// The MockPlugin maintains a list of requests that were mocked
$this->assertContainsOnly($request, $plugin->getReceivedRequests());
node.js web server for integration testing
------------------------------------------
Using mock responses is usually enough when testing a web service client. If your client needs to add custom cURL
options to requests, then you should use the node.js test web server to ensure that your HTTP request message is
being created correctly.
Guzzle is based around PHP's libcurl bindings. cURL sometimes modifies an HTTP request message based on
``CURLOPT_*`` options. Headers that are added to your request by cURL will not be accounted for if you inject mock
responses into your tests. Additionally, some request entity bodies cannot be loaded by the client before transmitting
it to the sever (for example, when using a client as a sort of proxy and streaming content from a remote server). You
might also need to inspect the entity body of a ``multipart/form-data`` POST request.
.. note::
You can skip all of the tests that require the node.js test web server by excluding the ``server`` group:
``phpunit --exclude-group server``
Using the test server
~~~~~~~~~~~~~~~~~~~~~
The node.js test server receives requests and returns queued responses. The test server exposes a simple API that is
used to enqueue responses and inspect the requests that it has received.
Retrieve the server object by calling ``$this->getServer()``. If the node.js server is not running, it will be
started as a forked process and an object that interfaces with the server will be returned. (note: stopping the
server is handled internally by Guzzle.)
You can queue an HTTP response or an array of responses by calling ``$this->getServer()->enqueue()``:
.. code-block:: php
$this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
The above code queues a single 200 response with an empty body. Responses are queued using a FIFO order; this
response will be returned by the server when it receives the first request and then removed from the queue. If a
request is received by a server with no queued responses, an exception will be thrown in your unit test.
You can inspect the requests that the server has retrieved by calling ``$this->getServer()->getReceivedRequests()``.
This method accepts an optional ``$hydrate`` parameter that specifies if you are retrieving an array of string HTTP
requests or an array of ``Guzzle\Http\RequestInterface`` subclassed objects. "Hydrating" the requests will allow
greater flexibility in your unit tests so that you can easily assert the state of the various parts of a request.
You will need to modify the base_url of your web service client in order to use it against the test server.
.. code-block:: php
$client = $this->getServiceBuilder()->get('my_client');
$client->setBaseUrl($this->getServer()->getUrl());
After running the above code, all calls made from the ``$client`` object will be sent to the test web server.
---
### Crmeb Vendor Guzzle Guzzle Docs Webservice Client Guzzle Service Descriptions (`crmeb/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst`)
===========================
Guzzle service descriptions
===========================
Guzzle allows you to serialize HTTP requests and parse HTTP responses using a DSL called a service descriptions.
Service descriptions define web service APIs by documenting each operation, the operation's parameters, validation
options for each parameter, an operation's response, how the response is parsed, and any errors that can be raised for
an operation. Writing a service description for a web service allows you to more quickly consume a web service than
writing concrete commands for each web service operation.
Guzzle service descriptions can be representing using a PHP array or JSON document. Guzzle's service descriptions are
heavily inspired by `Swagger <http://swagger.wordnik.com/>`_.
Service description schema
==========================
A Guzzle Service description must match the following JSON schema document. This document can also serve as a guide when
implementing a Guzzle service description.
Download the schema here: :download:`Guzzle JSON schema document </_downloads/guzzle-schema-1.0.json>`
.. class:: overflow-height-500px
.. literalinclude:: ../_downloads/guzzle-schema-1.0.json
:language: json
Top-level attributes
--------------------
Service descriptions are comprised of the following top-level attributes:
.. code-block:: json
{
"name": "string",
"apiVersion": "string|number",
"baseUrl": "string",
"description": "string",
"operations": {},
"models": {},
"includes": ["string.php", "string.json"]
}
+-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+
| Property Name | Value | Description |
+=========================================+=========================+=======================================================================================================================+
| name | string | Name of the web service |
+-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+
| apiVersion | string|number | Version identifier that the service description is compatible with |
+-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+
| baseUrl or basePath | string | Base URL of the web service. Any relative URI specified in an operation will be merged with the baseUrl using the |
| | | process defined in RFC 2396. Some clients require custom logic to determine the baseUrl. In those cases, it is best |
| | | to not include a baseUrl in the service description, but rather allow the factory method of the client to configure |
| | | the client’s baseUrl. |
+-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+
| description | string | Short summary of the web service |
+-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+
| operations | object containing | Operations of the service. The key is the name of the operation and value is the attributes of the operation. |
| | :ref:`operation-schema` | |
| | | |
+-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+
| models | object containing | Schema models that can be referenced throughout the service description. Models can be used to define how an HTTP |
| | :ref:`model-schema` | response is parsed into a ``Guzzle\Service\Resource\Model`` object when an operation uses a ``model`` ``responseType``|
+-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+
| includes | array of .js, | Service description files to include and extend from (can be a .json, .js, or .php file) |
| | .json, or .php | |
| | files. | |
+-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+
| (any additional properties) | mixed | Any additional properties specified as top-level attributes are allowed and will be treated as arbitrary data |
+-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+
.. _operation-schema:
Operations
----------
Operations are the actions that can be taken on a service. Each operation is given a unique name and has a distinct
endpoint and HTTP method. If an API has a ``DELETE /users/:id`` operation, a satisfactory operation name might be
``DeleteUser`` with a parameter of ``id`` that is inserted into the URI.
.. class:: overflow-height-250px
.. code-block:: json
{
"operations": {
"operationName": {
"extends": "string",
"httpMethod": "GET|POST|PUT|DELETE|PATCH|string",
"uri": "string",
"summary": "string",
"class": "string",
"responseClass": "string",
"responseNotes": "string",
"type": "string",
"description": "string",
"responseType": "primitive|class|(model by name)|documentation|(string)",
"deprecated": false,
"errorResponses": [
{
"code": 500,
"reason": "Unexpected Error",
"class": "string"
}
],
"data": {
"foo": "bar",
"baz": "bam"
},
"parameters": {}
}
}
}
.. csv-table::
:header: "Property Name", "Value", "Description"
:widths: 20, 15, 65
"extends", "string", "Extend from another operation by name. The parent operation must be defined before the child."
"httpMethod", "string", "HTTP method used with the operation (e.g. GET, POST, PUT, DELETE, PATCH, etc)"
"uri", "string", "URI of the operation. The uri attribute can contain URI templates. The variables of the URI template are parameters of the operation with a location value of uri"
"summary", "string", "Short summary of what the operation does"
"class", "string", "Custom class to instantiate instead of the default Guzzle\\Service\\Command\\OperationCommand. Using this attribute allows you to define an operation using a service description, but allows more customized logic to be implemented in user-land code."
"responseClass", "string", "Defined what is returned from the method. Can be a primitive, class name, or model name. You can specify the name of a class to return a more customized result from the operation (for example, a domain model object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``."
"responseNotes", "string", "A description of the response returned by the operation"
"responseType", "string", "The type of response that the operation creates: one of primitive, class, model, or documentation. If not specified, this value will be automatically inferred based on whether or not there is a model matching the name, if a matching class name is found, or set to 'primitive' by default."
"deprecated", "boolean", "Whether or not the operation is deprecated"
"errorResponses", "array", "Errors that could occur while executing the operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error), 'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this error is encountered)"
"data", "object", "Any arbitrary data to associate with the operation"
"parameters", "object containing :ref:`parameter-schema` objects", "Parameters of the operation. Parameters are used to define how input data is serialized into a HTTP request."
"additionalParameters", "A single :ref:`parameter-schema` object", "Validation and serialization rules for any parameter supplied to the operation that was not explicitly defined."
additionalParameters
~~~~~~~~~~~~~~~~~~~~
When a webservice offers a large number of parameters that all are set in the same location (for example the query
string or a JSON document), defining each parameter individually can require a lot of time and repetition. Furthermore,
some web services allow for completely arbitrary parameters to be supplied for an operation. The
``additionalParameters`` attribute can be used to solve both of these issues.
As an example, we can define a Twitter API operation quite easily using ``additionalParameters``. The
GetMentions operation accepts a large number of query string parameters. Defining each of these parameters
is ideal because it provide much more introspection for the client and opens the possibility to use the description with
other tools (e.g. a documentation generator). However, you can very quickly provide a "catch-all" serialization rule
that will place any custom parameters supplied to an operation the generated request's query string parameters.
.. class:: overflow-height-250px
.. code-block:: json
{
"name": "Twitter",
"apiVersion": "1.1",
"baseUrl": "https://api.twitter.com/1.1",
"operations": {
"GetMentions": {
"httpMethod": "GET",
"uri": "statuses/mentions_timeline.json",
"responseClass": "GetMentionsOutput",
"additionalParameters": {
"location": "query"
}
}
},
"models": {
"GetMentionsOutput": {
"type": "object",
"additionalProperties": {
"location": "json"
}
}
}
}
responseClass
~~~~~~~~~~~~~
The ``responseClass`` attribute is used to define the return value of an operation (what is returned by calling the
``getResult()`` method of a command object). The value set in the responseClass attribute can be one of "primitive"
(meaning the result with be primitive type like a string), a class name meaning the result will be an instance of a
specific user-land class, or a model name meaning the result will be a ``Guzzle\Service\Resource\Model`` object that
uses a :ref:`model schema <model-schema>` to define how the HTTP response is parsed.
.. note::
Using a class name with a ``responseClass`` will only work if it is supported by the ``class`` that is instantiated
for the operation. Keep this in mind when specifying a custom ``class`` attribute that points to a custom
``Guzzle\Service\Command\CommandInterface`` class. The default ``class``,
``Guzzle\Service\Command\OperationCommand``, does support setting custom ``class`` attributes.
You can specify the name of a class to return a more customized result from the operation (for example, a domain model
object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``.
Here's a very simple example of implementing a custom responseClass object.
.. code-block:: json
{
"operations": {
"test": {
"responseClass": "MyApplication\\User"
}
}
}
.. code-block:: php
namespace MyApplication;
use Guzzle\Service\Command\ResponseClassInterface;
use Guzzle\Service\Command\OperationCommand;
class User implements ResponseClassInterface
{
protected $name;
public static function fromCommand(OperationCommand $command)
{
$response = $command->getResponse();
$xml = $response->xml();
return new self((string) $xml->name);
}
public function __construct($name)
{
$this->name = $name;
}
}
errorResponses
~~~~~~~~~~~~~~
``errorResponses`` is an array containing objects that define the errors that could occur while executing the
operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error),
'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this
error is encountered).
ErrorResponsePlugin
^^^^^^^^^^^^^^^^^^^
Error responses are by default only used for documentation. If you don't need very complex exception logic for your web
service errors, then you can use the ``Guzzle\Plugin\ErrorResponse\ErrorResponsePlugin`` to automatically throw defined
exceptions when one of the ``errorResponse`` rules are matched. The error response plugin will listen for the
``request.complete`` event of a request created by a command object. Every response (including a successful response) is
checked against the list of error responses for an exact match using the following order of checks:
1. Does the errorResponse have a defined ``class``?
2. Is the errorResponse ``code`` equal to the status code of the response?
3. Is the errorResponse ``reason`` equal to the reason phrase of the response?
4. Throw the exception stored in the ``class`` attribute of the errorResponse.
The ``class`` attribute must point to a class that implements
``Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface``. This interface requires that an error response class
implements ``public static function fromCommand(CommandInterface $command, Response $response)``. This method must
return an object that extends from ``\Exception``. After an exception is returned, it is thrown by the plugin.
.. _parameter-schema:
Parameter schema
----------------
Parameters in both operations and models are represented using the
`JSON schema <http://tools.ietf.org/id/draft-zyp-json-schema-04.html>`_ syntax.
.. csv-table::
:header: "Property Name", "Value", "Description"
:widths: 20, 15, 65
"name", "string", "Unique name of the parameter"
"type", "string|array", "Type of variable (string, number, integer, boolean, object, array, numeric, null, any). Types are using for validation and determining the structure of a parameter. You can use a union type by providing an array of simple types. If one of the union types matches the provided value, then the value is valid."
"instanceOf", "string", "When the type is an object, you can specify the class that the object must implement"
"required", "boolean", "Whether or not the parameter is required"
"default", "mixed", "Default value to use if no value is supplied"
"static", "boolean", "Set to true to specify that the parameter value cannot be changed from the default setting"
"description", "string", "Documentation of the parameter"
"location", "string", "The location of a request used to apply a parameter. Custom locations can be registered with a command, but the defaults are uri, query, statusCode, reasonPhrase, header, body, json, xml, postField, postFile, responseBody"
"sentAs", "string", "Specifies how the data being modeled is sent over the wire. For example, you may wish to include certain headers in a response model that have a normalized casing of FooBar, but the actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar."
"filters", "array", "Array of functions to to run a parameter value through."
filters
~~~~~~~
Each value in the array must be a string containing the full class path to a static method or an array of complex
filter information. You can specify static methods of classes using the full namespace class name followed by
"::" (e.g. ``FooBar::baz()``). Some filters require arguments in order to properly filter a value. For complex filters,
use an object containing a ``method`` attribute pointing to a function, and an ``args`` attribute containing an
array of positional arguments to pass to the function. Arguments can contain keywords that are replaced when filtering
a value: ``@value`` is replaced with the value being filtered, and ``@api`` is replaced with the actual Parameter
object.
.. code-block:: json
{
"filters": [
"strtolower",
{
"method": "MyClass::convertString",
"args": [ "test", "@value", "@api" ]
}
]
}
The above example will filter a parameter using ``strtolower``. It will then call the ``convertString`` static method
of ``MyClass``, passing in "test", the actual value of the parameter, and a ``Guzzle\Service\Description\Parameter``
object.
Operation parameter location attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The location field of top-level parameters control how a parameter is serialized when generating a request.
uri location
^^^^^^^^^^^^
Parameters are injected into the ``uri`` attribute of the operation using
`URI-template expansion <http://tools.ietf.org/html/rfc6570>`_.
.. code-block:: json
{
"operations": {
"uriTest": {
"uri": "/test/{testValue}",
"parameters": {
"testValue": {
"location": "uri"
}
}
}
}
}
query location
^^^^^^^^^^^^^^
Parameters are injected into the query string of a request. Query values can be nested, which would result in a PHP
style nested query string. The name of a parameter is the default name of the query string parameter added to the
request. You can override this behavior by specifying the ``sentAs`` attribute on the parameter.
.. code-block:: json
{
"operations": {
"queryTest": {
"parameters": {
"testValue": {
"location": "query",
"sentAs": "test_value"
}
}
}
}
}
header location
^^^^^^^^^^^^^^^
Parameters are injected as headers on an HTTP request. The name of the parameter is used as the name of the header by
default. You can change the name of the header created by the parameter using the ``sentAs`` attribute.
Headers that are of type ``object`` will be added as multiple headers to a request using the key of the input array as
the header key. Setting a ``sentAs`` attribute along with a type ``object`` will use the value of ``sentAs`` as a
prefix for each header key.
body location
^^^^^^^^^^^^^
Parameters are injected as the body of a request. The input of these parameters may be anything that can be cast to a
string or a ``Guzzle\Http\EntityBodyInterface`` object.
postField location
^^^^^^^^^^^^^^^^^^
Parameters are inserted as POST fields in a request. Nested values may be supplied and will be represented using
PHP style nested query strings. The POST field name is the same as the parameter name by default. You can use the
``sentAs`` parameter to override the POST field name.
postFile location
^^^^^^^^^^^^^^^^^
Parameters are added as POST files. A postFile value may be a string pointing to a local filename or a
``Guzzle\Http\Message\PostFileInterface`` object. The name of the POST file will be the name of the parameter by
default. You can use a custom POST file name by using the ``sentAs`` attribute.
Supports "string" and "array" types.
json location
^^^^^^^^^^^^^
Parameters are added to the body of a request as top level keys of a JSON document. Nested values may be specified,
with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When JSON parameters are specified, the
``Content-Type`` of the request will change to ``application/json`` if a ``Content-Type`` has not already been specified
on the request.
xml location
^^^^^^^^^^^^
Parameters are added to the body of a request as top level nodes of an XML document. Nested values may be specified,
with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When XML parameters are specified, the
``Content-Type`` of the request will change to ``application/xml`` if a ``Content-Type`` has not already been specified
on the request.
responseBody location
^^^^^^^^^^^^^^^^^^^^^
Specifies the EntityBody of a response. This can be used to download the response body to a file or a custom Guzzle
EntityBody object.
No location
^^^^^^^^^^^
If a parameter has no location attribute, then the parameter is simply used as a data value.
Other locations
^^^^^^^^^^^^^^^
Custom locations can be registered as new locations or override default locations if needed.
.. _model-schema:
Model Schema
------------
Models are used in service descriptions to provide generic JSON schema definitions that can be extended from or used in
``$ref`` attributes. Models can also be referenced in a ``responseClass`` attribute to provide valuable output to an
operation. Models are JSON schema documents and use the exact syntax and attributes used in parameters.
Response Models
~~~~~~~~~~~~~~~
Response models describe how a response is parsed into a ``Guzzle\Service\Resource\Model`` object. Response models are
always modeled as JSON schema objects. When an HTTP response is parsed using a response model, the rules specified on
each property of a response model will translate 1:1 as keys in a PHP associative array. When a ``sentAs`` attribute is
found in response model parameters, the value retrieved from the HTTP response is retrieved using the ``sentAs``
parameter but stored in the response model using the name of the parameter.
The location field of top-level parameters in a response model tell response parsers how data is retrieved from a
response.
statusCode location
^^^^^^^^^^^^^^^^^^^
Retrieves the status code of the response.
reasonPhrase location
^^^^^^^^^^^^^^^^^^^^^
Retrieves the reason phrase of the response.
header location
^^^^^^^^^^^^^^^
Retrieves a header from the HTTP response.
body location
^^^^^^^^^^^^^
Retrieves the body of an HTTP response.
json location
^^^^^^^^^^^^^
Retrieves a top-level parameter from a JSON document contained in an HTTP response.
You can use ``additionalProperties`` if the JSON document is wrapped in an outer array. This allows you to parse the
contents of each item in the array using the parsing rules defined in the ``additionalProperties`` schema.
xml location
^^^^^^^^^^^^
Retrieves a top-level node value from an XML document contained in an HTTP response.
Other locations
^^^^^^^^^^^^^^^
Custom locations can be registered as new locations or override default locations if needed.
Example service description
---------------------------
Let's say you're interacting with a web service called 'Foo' that allows for the following routes and methods::
GET/POST /users
GET/DELETE /users/:id
The following JSON service description implements this simple web service:
.. class:: overflow-height-500px
.. code-block:: json
{
"name": "Foo",
"apiVersion": "2012-10-14",
"baseUrl": "http://api.foo.com",
"description": "Foo is an API that allows you to Baz Bar",
"operations": {
"GetUsers": {
"httpMethod": "GET",
"uri": "/users",
"summary": "Gets a list of users",
"responseClass": "GetUsersOutput"
},
"CreateUser": {
"httpMethod": "POST",
"uri": "/users",
"summary": "Creates a new user",
"responseClass": "CreateUserOutput",
"parameters": {
"name": {
"location": "json",
"type": "string"
},
"age": {
"location": "json",
"type": "integer"
}
}
},
"GetUser": {
"httpMethod": "GET",
"uri": "/users/{id}",
"summary": "Retrieves a single user",
"responseClass": "GetUserOutput",
"parameters": {
"id": {
"location": "uri",
"description": "User to retrieve by ID",
"required": true
}
}
},
"DeleteUser": {
"httpMethod": "DELETE",
"uri": "/users/{id}",
"summary": "Deletes a user",
"responseClass": "DeleteUserOutput",
"parameters": {
"id": {
"location": "uri",
"description": "User to delete by ID",
"required": true
}
}
}
},
"models": {
"GetUsersOutput": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"location": "json",
"type": "string"
},
"age": {
"location": "json",
"type": "integer"
}
}
}
},
"CreateUserOutput": {
"type": "object",
"properties": {
"id": {
"location": "json",
"type": "string"
},
"location": {
"location": "header",
"sentAs": "Location",
"type": "string"
}
}
},
"GetUserOutput": {
"type": "object",
"properties": {
"name": {
"location": "json",
"type": "string"
},
"age": {
"location": "json",
"type": "integer"
}
}
},
"DeleteUserOutput": {
"type": "object",
"properties": {
"status": {
"location": "statusCode",
"type": "integer"
}
}
}
}
}
If you attach this service description to a client, you would completely configure the client to interact with the
Foo web service and provide valuable response models for each operation.
.. code-block:: php
use Guzzle\Service\Description\ServiceDescription;
$description = ServiceDescription::factory('/path/to/client.json');
$client->setDescription($description);
$command = $client->getCommand('DeleteUser', array('id' => 123));
$responseModel = $client->execute($command);
echo $responseModel['status'];
.. note::
You can add the service description to your client's factory method or constructor.
---
### Crmeb Vendor Guzzle Guzzle Docs Webservice Client Using The Service Builder (`crmeb/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst`)
=======================
Using a service builder
=======================
The best way to instantiate Guzzle web service clients is to let Guzzle handle building the clients for you using a
ServiceBuilder. A ServiceBuilder is responsible for creating concrete client objects based on configuration settings
and helps to manage credentials for different environments.
You don't have to use a service builder, but they help to decouple your application from concrete classes and help to
share configuration data across multiple clients. Consider the following example. Here we are creating two clients that
require the same API public key and secret key. The clients are created using their ``factory()`` methods.
.. code-block:: php
use MyService\FooClient;
use MyService\BarClient;
$foo = FooClient::factory(array(
'key' => 'abc',
'secret' => '123',
'custom' => 'and above all'
));
$bar = BarClient::factory(array(
'key' => 'abc',
'secret' => '123',
'custom' => 'listen to me'
));
The redundant specification of the API keys can be removed using a service builder.
.. code-block:: php
use Guzzle\Service\Builder\ServiceBuilder;
$builder = ServiceBuilder::factory(array(
'services' => array(
'abstract_client' => array(
'params' => array(
'key' => 'abc',
'secret' => '123'
)
),
'foo' => array(
'extends' => 'abstract_client',
'class' => 'MyService\FooClient',
'params' => array(
'custom' => 'and above all'
)
),
'bar' => array(
'extends' => 'abstract_client',
'class' => 'MyService\FooClient',
'params' => array(
'custom' => 'listen to me'
)
)
)
));
$foo = $builder->get('foo');
$bar = $builder->get('bar');
You can make managing your API keys even easier by saving the service builder configuration in a JSON format in a
.json file.
Creating a service builder
--------------------------
A ServiceBuilder can source information from an array, an PHP include file that returns an array, or a JSON file.
.. code-block:: php
use Guzzle\Service\Builder\ServiceBuilder;
// Source service definitions from a JSON file
$builder = ServiceBuilder::factory('services.json');
Sourcing data from an array
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Data can be source from a PHP array. The array must contain an associative ``services`` array that maps the name of a
client to the configuration information used by the service builder to create the client. Clients are given names
which are used to identify how a client is retrieved from a service builder. This can be useful for using multiple
accounts for the same service or creating development clients vs. production clients.
.. code-block:: php
$services = array(
'includes' => array(
'/path/to/other/services.json',
'/path/to/other/php_services.php'
),
'services' => array(
'abstract.foo' => array(
'params' => array(
'username' => 'foo',
'password' => 'bar'
)
),
'bar' => array(
'extends' => 'abstract.foo',
'class' => 'MyClientClass',
'params' => array(
'other' => 'abc'
)
)
)
);
A service builder configuration array contains two top-level array keys:
+------------+---------------------------------------------------------------------------------------------------------+
| Key | Description |
+============+=========================================================================================================+
| includes | Array of paths to JSON or PHP include files to include in the configuration. |
+------------+---------------------------------------------------------------------------------------------------------+
| services | Associative array of defined services that can be created by the service builder. Each service can |
| | contain the following keys: |
| | |
| | +------------+----------------------------------------------------------------------------------------+ |
| | | Key | Description | |
| | +============+========================================================================================+ |
| | | class | The concrete class to instantiate that implements the | |
| | | | ``Guzzle\Common\FromConfigInterface``. | |
| | +------------+----------------------------------------------------------------------------------------+ |
| | | extends | The name of a previously defined service to extend from | |
| | +------------+----------------------------------------------------------------------------------------+ |
| | | params | Associative array of parameters to pass to the factory method of the service it is | |
| | | | instantiated | |
| | +------------+----------------------------------------------------------------------------------------+ |
| | | alias | An alias that can be used in addition to the array key for retrieving a client from | |
| | | | the service builder. | |
| | +------------+----------------------------------------------------------------------------------------+ |
+------------+---------------------------------------------------------------------------------------------------------+
The first client defined, ``abstract.foo``, is used as a placeholder of shared configuration values. Any service
extending abstract.foo will inherit its params. As an example, this can be useful when clients share the same username
and password.
The next client, ``bar``, extends from ``abstract.foo`` using the ``extends`` attribute referencing the client from
which to extend. Additional parameters can be merged into the original service definition when extending a parent
service.
.. important::
Each client that you intend to instantiate must specify a ``class`` attribute that references the full class name
of the client being created. The class referenced in the ``class`` parameter must implement a static ``factory()``
method that accepts an array or ``Guzzle\Common\Collection`` object and returns an instantiated object.
Sourcing from a PHP include
~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can create service builder configurations using a PHP include file. This can be useful if you wish to take
advantage of an opcode cache like APC to speed up the process of loading and processing the configuration. The PHP
include file is the same format as an array, but you simply create a PHP script that returns an array and save the
file with the .php file extension.
.. code-block:: php
<?php return array('services' => '...');
// Saved as config.php
This configuration file can then be used with a service builder.
.. code-block:: php
$builder = ServiceBuilder::factory('/path/to/config.php');
Sourcing from a JSON document
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can use JSON documents to serialize your service descriptions. The JSON format uses the exact same structure as
the PHP array syntax, but it's just serialized using JSON.
.. code-block:: javascript
{
"includes": ["/path/to/other/services.json", "/path/to/other/php_services.php"],
"services": {
"abstract.foo": {
"params": {
"username": "foo",
"password": "bar"
}
},
"bar": {
"extends": "abstract.foo",
"class": "MyClientClass",
"params": {
"other": "abc"
}
}
}
}
Referencing other clients in parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If one of your clients depends on another client as one of its parameters, you can reference that client by name by
enclosing the client's reference key in ``{}``.
.. code-block:: javascript
{
"services": {
"token": {
"class": "My\Token\TokenFactory",
"params": {
"access_key": "xyz"
}
},
"client": {
"class": "My\Client",
"params": {
"token_client": "{token}",
"version": "1.0"
}
}
}
}
When ``client`` is constructed by the service builder, the service builder will first create the ``token`` service
and then inject the token service into ``client``'s factory method in the ``token_client`` parameter.
Retrieving clients from a service builder
-----------------------------------------
Clients are referenced using a customizable name you provide in your service definition. The ServiceBuilder is a sort
of multiton object-- it will only instantiate a client once and return that client for subsequent retrievals. Clients
are retrieved by name (the array key used in the configuration) or by the ``alias`` setting of a service.
Here's an example of retrieving a client from your ServiceBuilder:
.. code-block:: php
$client = $builder->get('foo');
// You can also use the ServiceBuilder object as an array
$client = $builder['foo'];
Creating throwaway clients
~~~~~~~~~~~~~~~~~~~~~~~~~~
You can get a "throwaway" client (a client that is not persisted by the ServiceBuilder) by passing ``true`` in the
second argument of ``ServiceBuilder::get()``. This allows you to create a client that will not be returned by other
parts of your code that use the service builder. Instead of passing ``true``, you can pass an array of configuration
settings that will override the configuration settings specified in the service builder.
.. code-block:: php
// Get a throwaway client and overwrite the "custom" setting of the client
$foo = $builder->get('foo', array(
'custom' => 'in this world there are rules'
));
Getting raw configuration settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can get the raw configuration settings provided to the service builder for a specific service using the
``getData($name)`` method of a service builder. This method will null if the service was not found in the service
builder or an array of configuration settings if the service was found.
.. code-block:: php
$data = $builder->getData('foo');
echo $data['key'] . "\n";
echo $data['secret'] . "\n";
echo $data['custom'] . "\n";
Adding a plugin to all clients
------------------------------
You can add a plugin to all clients created by a service builder using the ``addGlobalPlugin($plugin)`` method of a
service builder and passing a ``Symfony\Component\EventDispatcher\EventSubscriberInterface`` object. The service builder
will then attach each global plugin to every client as it is created. This allows you to, for example, add a LogPlugin
to every request created by a service builder for easy debugging.
.. code-block:: php
use Guzzle\Plugin\Log\LogPlugin;
// Add a debug log plugin to every client as it is created
$builder->addGlobalPlugin(LogPlugin::getDebugPlugin());
$foo = $builder->get('foo');
$foo->get('/')->send();
// Should output all of the data sent over the wire
.. _service-builder-events:
Events emitted from a service builder
-------------------------------------
A ``Guzzle\Service\Builder\ServiceBuilder`` object emits the following events:
+-------------------------------+--------------------------------------------+-----------------------------------------+
| Event name | Description | Event data |
+===============================+============================================+=========================================+
| service_builder.create_client | Called when a client is created | * client: The created client object |
+-------------------------------+--------------------------------------------+-----------------------------------------+
.. code-block:: php
use Guzzle\Common\Event;
use Guzzle\Service\Builder\ServiceBuilder;
$builder = ServiceBuilder::factory('/path/to/config.json');
// Add an event listener to print out each client client as it is created
$builder->getEventDispatcher()->addListener('service_builder.create_client', function (Event $e) {
echo 'Client created: ' . get_class($e['client']) . "\n";
});
$foo = $builder->get('foo');
// Should output the class used for the "foo" client
---
### Crmeb Vendor Guzzle Guzzle Docs Webservice Client Webservice Client (`crmeb/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst`)
======================
The web service client
======================
The ``Guzzle\Service`` namespace contains various abstractions that help to make it easier to interact with a web
service API, including commands, service descriptions, and resource iterators.
In this chapter, we'll build a simple `Twitter API client <https://dev.twitter.com/docs/api/1.1>`_.
Creating a client
=================
A class that extends from ``Guzzle\Service\Client`` or implements ``Guzzle\Service\ClientInterface`` must implement a
``factory()`` method in order to be used with a :doc:`service builder <using-the-service-builder>`.
Factory method
--------------
You can use the ``factory()`` method of a client directly if you do not need a service builder.
.. code-block:: php
use mtdowling\TwitterClient;
// Create a client and pass an array of configuration data
$twitter = TwitterClient::factory(array(
'consumer_key' => '****',
'consumer_secret' => '****',
'token' => '****',
'token_secret' => '****'
));
.. note::
If you'd like to follow along, here's how to get your Twitter API credentials:
1. Visit https://dev.twitter.com/apps
2. Click on an application that you've created
3. Click on the "OAuth tool" tab
4. Copy all of the settings under "OAuth Settings"
Implementing a factory method
-----------------------------
Creating a client and its factory method is pretty simple. You just need to implement ``Guzzle\Service\ClientInterface``
or extend from ``Guzzle\Service\Client``.
.. code-block:: php
namespace mtdowling;
use Guzzle\Common\Collection;
use Guzzle\Plugin\Oauth\OauthPlugin;
use Guzzle\Service\Client;
use Guzzle\Service\Description\ServiceDescription;
/**
* A simple Twitter API client
*/
class TwitterClient extends Client
{
public static function factory($config = array())
{
// Provide a hash of default client configuration options
$default = array('base_url' => 'https://api.twitter.com/1.1');
// The following values are required when creating the client
$required = array(
'base_url',
'consumer_key',
'consumer_secret',
'token',
'token_secret'
);
// Merge in default settings and validate the config
$config = Collection::fromConfig($config, $default, $required);
// Create a new Twitter client
$client = new self($config->get('base_url'), $config);
// Ensure that the OauthPlugin is attached to the client
$client->addSubscriber(new OauthPlugin($config->toArray()));
return $client;
}
}
Service Builder
---------------
A service builder is used to easily create web service clients, provides a simple configuration driven approach to
creating clients, and allows you to share configuration settings across multiple clients. You can find out more about
Guzzle's service builder in :doc:`using-the-service-builder`.
.. code-block:: php
use Guzzle\Service\Builder\ServiceBuilder;
// Create a service builder and provide client configuration data
$builder = ServiceBuilder::factory('/path/to/client_config.json');
// Get the client from the service builder by name
$twitter = $builder->get('twitter');
The above example assumes you have JSON data similar to the following stored in "/path/to/client_config.json":
.. code-block:: json
{
"services": {
"twitter": {
"class": "mtdowling\\TwitterClient",
"params": {
"consumer_key": "****",
"consumer_secret": "****",
"token": "****",
"token_secret": "****"
}
}
}
}
.. note::
A service builder becomes much more valuable when using multiple web service clients in a single application or
if you need to utilize the same client with varying configuration settings (e.g. multiple accounts).
Commands
========
Commands are a concept in Guzzle that helps to hide the underlying implementation of an API by providing an easy to use
parameter driven object for each action of an API. A command is responsible for accepting an array of configuration
parameters, serializing an HTTP request, and parsing an HTTP response. Following the
`command pattern <http://en.wikipedia.org/wiki/Command_pattern>`_, commands in Guzzle offer a greater level of
flexibility when implementing and utilizing a web service client.
Executing commands
------------------
You must explicitly execute a command after creating a command using the ``getCommand()`` method. A command has an
``execute()`` method that may be called, or you can use the ``execute()`` method of a client object and pass in the
command object. Calling either of these execute methods will return the result value of the command. The result value is
the result of parsing the HTTP response with the ``process()`` method.
.. code-block:: php
// Get a command from the client and pass an array of parameters
$command = $twitter->getCommand('getMentions', array(
'count' => 5
));
// Other parameters can be set on the command after it is created
$command['trim_user'] = false;
// Execute the command using the command object.
// The result value contains an array of JSON data from the response
$result = $command->execute();
// You can retrieve the result of the command later too
$result = $command->getResult().
Command object also contains methods that allow you to inspect the HTTP request and response that was utilized with
the command.
.. code-block:: php
$request = $command->getRequest();
$response = $command->getResponse();
.. note::
The format and notation used to retrieve commands from a client can be customized by injecting a custom command
factory, ``Guzzle\Service\Command\Factory\FactoryInterface``, on the client using ``$client->setCommandFactory()``.
Executing with magic methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When using method missing magic methods with a command, the command will be executed right away and the result of the
command is returned.
.. code-block:: php
$jsonData = $twitter->getMentions(array(
'count' => 5,
'trim_user' => true
));
Creating commands
-----------------
Commands are created using either the ``getCommand()`` method of a client or a magic missing method of a client. Using
the ``getCommand()`` method allows you to create a command without executing it, allowing for customization of the
command or the request serialized by the command.
When a client attempts to create a command, it uses the client's ``Guzzle\Service\Command\Factory\FactoryInterface``.
By default, Guzzle will utilize a command factory that first looks for a concrete class for a particular command
(concrete commands) followed by a command defined by a service description (operation commands). We'll learn more about
concrete commands and operation commands later in this chapter.
.. code-block:: php
// Get a command from the twitter client.
$command = $twitter->getCommand('getMentions');
$result = $command->execute();
Unless you've skipped ahead, running the above code will throw an exception.
PHP Fatal error: Uncaught exception 'Guzzle\Common\Exception\InvalidArgumentException' with message
'Command was not found matching getMentions'
This exception was thrown because the "getMentions" command has not yet been implemented. Let's implement one now.
Concrete commands
~~~~~~~~~~~~~~~~~
Commands can be created in one of two ways: create a concrete command class that extends
``Guzzle\Service\Command\AbstractCommand`` or
:doc:`create an OperationCommand based on a service description <guzzle-service-descriptions>`. The recommended
approach is to use a service description to define your web service, but you can use concrete commands when custom
logic must be implemented for marshaling or unmarshaling a HTTP message.
Commands are the method in which you abstract away the underlying format of the requests that need to be sent to take
action on a web service. Commands in Guzzle are meant to be built by executing a series of setter methods on a command
object. Commands are only validated right before they are executed. A ``Guzzle\Service\Client`` object is responsible
for executing commands. Commands created for your web service must implement
``Guzzle\Service\Command\CommandInterface``, but it's easier to extend the ``Guzzle\Service\Command\AbstractCommand``
class, implement the ``build()`` method, and optionally implement the ``process()`` method.
Serializing requests
^^^^^^^^^^^^^^^^^^^^
The ``build()`` method of a command is responsible for using the arguments of the command to build and serialize a
HTTP request and set the request on the ``$request`` property of the command object. This step is usually taken care of
for you when using a service description driven command that uses the default
``Guzzle\Service\Command\OperationCommand``. You may wish to implement the process method yourself when you aren't
using a service description or need to implement more complex request serialization.
.. important::::
When implementing a custom ``build()`` method, be sure to set the class property of ``$this->request`` to an
instantiated and ready to send request.
The following example shows how to implement the ``getMentions``
`Twitter API <https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline>`_ method using a concrete command.
.. code-block:: php
namespace mtdowling\Twitter\Command;
use Guzzle\Service\Command\AbstractCommand;
class GetMentions extends AbstractCommand
{
protected function build()
{
// Create the request property of the command
$this->request = $this->client->get('statuses/mentions_timeline.json');
// Grab the query object of the request because we will use it for
// serializing command parameters on the request
$query = $this->request->getQuery();
if ($this['count']) {
$query->set('count', $this['count']);
}
if ($this['since_id']) {
$query->set('since_id', $this['since_id']);
}
if ($this['max_id']) {
$query->set('max_id', $this['max_id']);
}
if ($this['trim_user'] !== null) {
$query->set('trim_user', $this['trim_user'] ? 'true' : 'false');
}
if ($this['contributor_details'] !== null) {
$query->set('contributor_details', $this['contributor_details'] ? 'true' : 'false');
}
if ($this['include_entities'] !== null) {
$query->set('include_entities', $this['include_entities'] ? 'true' : 'false');
}
}
}
By default, a client will attempt to find concrete command classes under the ``Command`` namespace of a client. First
the client will attempt to find an exact match for the name of the command to the name of the command class. If an
exact match is not found, the client will calculate a class name using inflection. This is calculated based on the
folder hierarchy of a command and converting the CamelCased named commands into snake_case. Here are some examples on
how the command names are calculated:
#. ``Foo\Command\JarJar`` **->** jar_jar
#. ``Foo\Command\Test`` **->** test
#. ``Foo\Command\People\GetCurrentPerson`` **->** people.get_current_person
Notice how any sub-namespace beneath ``Command`` is converted from ``\`` to ``.`` (a period). CamelCasing is converted
to lowercased snake_casing (e.g. JarJar == jar_jar).
Parsing responses
^^^^^^^^^^^^^^^^^
The ``process()`` method of a command is responsible for converting an HTTP response into something more useful. For
example, a service description operation that has specified a model object in the ``responseClass`` attribute of the
operation will set a ``Guzzle\Service\Resource\Model`` object as the result of the command. This behavior can be
completely modified as needed-- even if you are using operations and responseClass models. Simply implement a custom
``process()`` method that sets the ``$this->result`` class property to whatever you choose. You can reuse parts of the
default Guzzle response parsing functionality or get inspiration from existing code by using
``Guzzle\Service\Command\OperationResponseParser`` and ``Guzzle\Service\Command\DefaultResponseParser`` classes.
If you do not implement a custom ``process()`` method and are not using a service description, then Guzzle will attempt
to guess how a response should be processed based on the Content-Type header of the response. Because the Twitter API
sets a ``Content-Type: application/json`` header on this response, we do not need to implement any custom response
parsing.
Operation commands
~~~~~~~~~~~~~~~~~~
Operation commands are commands in which the serialization of an HTTP request and the parsing of an HTTP response are
driven by a Guzzle service description. Because request serialization, validation, and response parsing are
described using a DSL, creating operation commands is a much faster process than writing concrete commands.
Creating operation commands for our Twitter client can remove a great deal of redundancy from the previous concrete
command, and allows for a deeper runtime introspection of the API. Here's an example service description we can use to
create the Twitter API client:
.. code-block:: json
{
"name": "Twitter",
"apiVersion": "1.1",
"baseUrl": "https://api.twitter.com/1.1",
"description": "Twitter REST API client",
"operations": {
"GetMentions": {
"httpMethod": "GET",
"uri": "statuses/mentions_timeline.json",
"summary": "Returns the 20 most recent mentions for the authenticating user.",
"responseClass": "GetMentionsOutput",
"parameters": {
"count": {
"description": "Specifies the number of tweets to try and retrieve",
"type": "integer",
"location": "query"
},
"since_id": {
"description": "Returns results with an ID greater than the specified ID",
"type": "integer",
"location": "query"
},
"max_id": {
"description": "Returns results with an ID less than or equal to the specified ID.",
"type": "integer",
"location": "query"
},
"trim_user": {
"description": "Limits the amount of data returned for each user",
"type": "boolean",
"location": "query"
},
"contributor_details": {
"description": "Adds more data to contributor elements",
"type": "boolean",
"location": "query"
},
"include_entities": {
"description": "The entities node will be disincluded when set to false.",
"type": "boolean",
"location": "query"
}
}
}
},
"models": {
"GetMentionsOutput": {
"type": "object",
"additionalProperties": {
"location": "json"
}
}
}
}
If you're lazy, you can define the API in a less descriptive manner using ``additionalParameters``.
``additionalParameters`` define the serialization and validation rules of parameters that are not explicitly defined
in a service description.
.. code-block:: json
{
"name": "Twitter",
"apiVersion": "1.1",
"baseUrl": "https://api.twitter.com/1.1",
"description": "Twitter REST API client",
"operations": {
"GetMentions": {
"httpMethod": "GET",
"uri": "statuses/mentions_timeline.json",
"summary": "Returns the 20 most recent mentions for the authenticating user.",
"responseClass": "GetMentionsOutput",
"additionalParameters": {
"location": "query"
}
}
},
"models": {
"GetMentionsOutput": {
"type": "object",
"additionalProperties": {
"location": "json"
}
}
}
}
You should attach the service description to the client at the end of the client's factory method:
.. code-block:: php
// ...
class TwitterClient extends Client
{
public static function factory($config = array())
{
// ... same code as before ...
// Set the service description
$client->setDescription(ServiceDescription::factory('path/to/twitter.json'));
return $client;
}
}
The client can now use operations defined in the service description instead of requiring you to create concrete
command classes. Feel free to delete the concrete command class we created earlier.
.. code-block:: php
$jsonData = $twitter->getMentions(array(
'count' => 5,
'trim_user' => true
));
Executing commands in parallel
------------------------------
Much like HTTP requests, Guzzle allows you to send multiple commands in parallel. You can send commands in parallel by
passing an array of command objects to a client's ``execute()`` method. The client will serialize each request and
send them all in parallel. If an error is encountered during the transfer, then a
``Guzzle\Service\Exception\CommandTransferException`` is thrown, which allows you to retrieve a list of commands that
succeeded and a list of commands that failed.
.. code-block:: php
use Guzzle\Service\Exception\CommandTransferException;
$commands = array();
$commands[] = $twitter->getCommand('getMentions');
$commands[] = $twitter->getCommand('otherCommandName');
// etc...
try {
$result = $client->execute($commands);
foreach ($result as $command) {
echo $command->getName() . ': ' . $command->getResponse()->getStatusCode() . "\n";
}
} catch (CommandTransferException $e) {
// Get an array of the commands that succeeded
foreach ($e->getSuccessfulCommands() as $command) {
echo $command->getName() . " succeeded\n";
}
// Get an array of the commands that failed
foreach ($e->getFailedCommands() as $command) {
echo $command->getName() . " failed\n";
}
}
.. note::
All commands executed from a client using an array must originate from the same client.
Special command options
-----------------------
Guzzle exposes several options that help to control how commands are validated, serialized, and parsed.
Command options can be specified when creating a command or in the ``command.params`` parameter in the
``Guzzle\Service\Client``.
=========================== ============================================================================================
command.request_options Option used to add :ref:`Request options <request-options>` to the request created by a
command
command.hidden_params An array of the names of parameters ignored by the ``additionalParameters`` parameter schema
command.disable_validation Set to true to disable JSON schema validation of the command's input parameters
command.response_processing Determines how the default response parser will parse the command. One of "raw" no parsing,
"model" (the default method used to parse commands using response models defined in service
descriptions)
command.headers (deprecated) Option used to specify custom headers. Use ``command.request_options`` instead
command.on_complete (deprecated) Option used to add an onComplete method to a command. Use
``command.after_send`` event instead
command.response_body (deprecated) Option used to change the entity body used to store a response.
Use ``command.request_options`` instead
=========================== ============================================================================================
Advanced client configuration
=============================
Default command parameters
--------------------------
When creating a client object, you can specify default command parameters to pass into all commands. Any key value pair
present in the ``command.params`` settings of a client will be added as default parameters to any command created
by the client.
.. code-block:: php
$client = new Guzzle\Service\Client(array(
'command.params' => array(
'default_1' => 'foo',
'another' => 'bar'
)
));
Magic methods
-------------
Client objects will, by default, attempt to create and execute commands when a missing method is invoked on a client.
This powerful concept applies to both concrete commands and operation commands powered by a service description. This
makes it appear to the end user that you have defined actual methods on a client object, when in fact, the methods are
invoked using PHP's magic ``__call`` method.
The ``__call`` method uses the ``getCommand()`` method of a client, which uses the client's internal
``Guzzle\Service\Command\Factory\FactoryInterface`` object. The default command factory allows you to instantiate
operations defined in a client's service description. The method in which a client determines which command to
execute is defined as follows:
1. The client will first try to find a literal match for an operation in the service description.
2. If the literal match is not found, the client will try to uppercase the first character of the operation and find
the match again.
3. If a match is still not found, the command factory will inflect the method name from CamelCase to snake_case and
attempt to find a matching command.
4. If a command still does not match, an exception is thrown.
.. code-block:: php
// Use the magic method
$result = $twitter->getMentions();
// This is exactly the same as:
$result = $twitter->getCommand('getMentions')->execute();
You can disable magic methods on a client by passing ``false`` to the ``enableMagicMethod()`` method.
Custom command factory
----------------------
A client by default uses the ``Guzzle\Service\Command\Factory\CompositeFactory`` which allows multiple command
factories to attempt to create a command by a certain name. The default CompositeFactory uses a ``ConcreteClassFactory``
and a ``ServiceDescriptionFactory`` if a service description is specified on a client. You can specify a custom
command factory if your client requires custom command creation logic using the ``setCommandFactory()`` method of
a client.
Custom resource Iterator factory
--------------------------------
Resource iterators can be retrieved from a client using the ``getIterator($name)`` method of a client. This method uses
a client's internal ``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object. A client by default uses a
``Guzzle\Service\Resource\ResourceIteratorClassFactory`` to attempt to find concrete classes that implement resource
iterators. The default factory will first look for matching iterators in the ``Iterator`` subdirectory of the client
followed by the ``Model`` subdirectory of a client. Use the ``setResourceIteratorFactory()`` method of a client to
specify a custom resource iterator factory.
Plugins and events
==================
``Guzzle\Service\Client`` exposes various events that allow you to hook in custom logic. A client object owns a
``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling
``$client->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or
event subscribers (classes that listen to specific events of a dispatcher).
.. _service-client-events:
Events emitted from a Service Client
------------------------------------
A ``Guzzle\Service\Client`` object emits the following events:
+------------------------------+--------------------------------------------+------------------------------------------+
| Event name | Description | Event data |
+==============================+============================================+==========================================+
| client.command.create | The client created a command object | * client: Client object |
| | | * command: Command object |
+------------------------------+--------------------------------------------+------------------------------------------+
| command.before_prepare | Before a command is validated and built. | * command: Command being prepared |
| | This is also before a request is created. | |
+------------------------------+--------------------------------------------+------------------------------------------+
| command.after_prepare | After a command instantiates and | * command: Command that was prepared |
| | configures its request object. | |
+------------------------------+--------------------------------------------+------------------------------------------+
| command.before_send | The client is about to execute a prepared | * command: Command to execute |
| | command | |
+------------------------------+--------------------------------------------+------------------------------------------+
| command.after_send | The client successfully completed | * command: The command that was executed |
| | executing a command | |
+------------------------------+--------------------------------------------+------------------------------------------+
| command.parse_response | Called when ``responseType`` is ``class`` | * command: The command with a response |
| | and the response is about to be parsed. | about to be parsed. |
+------------------------------+--------------------------------------------+------------------------------------------+
.. code-block:: php
use Guzzle\Common\Event;
use Guzzle\Service\Client;
$client = new Client();
// create an event listener that operates on request objects
$client->getEventDispatcher()->addListener('command.after_prepare', function (Event $event) {
$command = $event['command'];
$request = $command->getRequest();
// do something with request
});
.. code-block:: php
use Guzzle\Common\Event;
use Guzzle\Common\Client;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class EventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
'client.command.create' => 'onCommandCreate',
'command.parse_response' => 'onParseResponse'
);
}
public function onCommandCreate(Event $event)
{
$client = $event['client'];
$command = $event['command'];
// operate on client and command
}
public function onParseResponse(Event $event)
{
$command = $event['command'];
// operate on the command
}
}
$client = new Client();
$client->addSubscriber(new EventSubscriber());
---
--- METRICS ---
- Files Extracted: 38
- Estimated Token Budget: ~66230 tokens
- Canonical Reference: https://codewiki.google/github.com/crmeb/CRMEB