PHP utility collection with hybrid and fluent APIs.

⌈⌋ ⎇ branch:  hybrid7 libraries


Artifact [505882b837]

Artifact 505882b83712c43c16682fed2b93c307d2077b5d:

  • File curl.php — part of check-in [49b2714f5f] at 2014-08-14 07:39:53 on branch trunk — Added static $defaults[] array instead of built-in constructor calls. Introduces ->assert() to test CURL/HTTP properties after ->exec(), and possibly drop the result on mismatches. (user: mario size: 6496)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
   100
   101
   102
   103
   104
   105
   106
   107
   108
   109
   110
   111
   112
   113
   114
   115
   116
   117
   118
   119
   120
   121
   122
   123
   124
   125
   126
   127
   128
   129
   130
   131
   132
   133
   134
   135
   136
   137
   138
   139
   140
   141
   142
   143
   144
   145
   146
   147
   148
   149
   150
   151
   152
   153
   154
   155
   156
   157
   158
   159
   160
   161
   162
   163
   164
   165
   166
   167
   168
   169
   170
   171
   172
   173
   174
   175
   176
   177
   178
   179
   180
   181
   182
   183
   184
   185
   186
   187
   188
   189
   190
   191
   192
   193
   194
   195
   196
   197
   198
   199
   200
   201
   202
   203
   204
   205
   206
   207
   208
   209
   210
   211
   212
   213
   214
   215
   216
   217
   218
   219
   220
   221
   222
   223
   224
   225
   226
   227
   228
   229
   230
   231
   232
   233
   234
   235
   236
   237
   238
   239
   240
   241
   242
   243
   244
   245
   246
   247
   248
   249
   250
   251
   252
   253
   254
   255
   256
   257
   258
   259
   260
   261
   262
   263
   264
   265
   266
   267
   268
   269
   270
   271
   272
   273
   274
   275
   276
   277
   278
   279
   280
   281
   282
   283
<?php
/**
 * api: php7
 * title: fluent curl
 * description: simple wrapper around curl functions
 * version: 0.3
 * license: Public Domain
 *
 *
 * This simple wrapper class and its factory function allow
 * compact invocations of curl. You can pass either just an
 * URL, an option array, a previously wrapped curl() or a
 * raw curl resource handle.
 * Methods and options (CURL_ constants) lose their prefix
 * once wrapped, and can be easily chained:
 *
 *   curl($url)->followlocation(1)->verbose(1)->exec();
 *
 * Option names are case-insensitive too. The exec() and any
 * setting retrieval terminate the fluent call.
 *
 *
 * Individual curl() wrappers can also be combined into a
 * parallel-request curl_multi() instance:
 *
 *   curl_multi($url1, $url2, $url3)->verbose(1)->exec();
 *
 * Here the handles may be passed as associative array or
 * indexed list. exec() will wait until all finish, then
 * directly return an array of result contents.
 * In-between option calls are applied to all bound handles.
 *
 */



# hybrid constructor
function curl($url=NULL) {
    return is_a($url, "curl") ? $url : new curl($url);
}



/**
 * Fluent curl wrapper which obviates CURL_ prefixes.
 *
 * Makes curl_functions available as methods. Previous constants
 * are easily accessible through chainable method calls as well.
 * Only getinfo() options are mapped as virtual properties.
 *
 */
class curl {


    /**
     * resource / curl handle
     *
     */
    public $handle = NULL;
    
    
    /**
     * Setup parameters.
     *
     */
    static $defaults = array(
        "returntransfer" => 1,
        "httpget" => 1,
        "http200aliases" => array(200, 201, 202, 203),
        "header" => 0,
    );


    /**
     * Some checks before returning the resulting content.
     *
     */
    public $assert = array();


    /**
     * Initialize, where $params is usually just an $url, or an [OPT=>val] list
     *
     */
    function __construct($params) {
    
        // create
        $this->handle = is_object($params) ? $params : curl_init();
        
        // merge options
        $params = array_merge(
            curl::$defaults,
            array("followlocation" => !ini_get("safe_mode")),
            is_array($params) ? array_change_key_case($params) : array(),
            is_string($params) ? array("url" => $params) : array()
        );

        // multiple [url=>$url, post=>$bool, ..] options
        if (is_array($params)) foreach ($params as $cmd=>$opt) {
            $this->__call($cmd, array($opt));
        }
    }

    
    /**
     * Most invocations are mapped onto CURL_CONST settings,
     * while some are just plain function calls:
     *
     * @method exec()
     * @method errno()
     * @method error()
     * @method getinfo()
     */
    function __call($opt, $args) {

        # CURLOPT_*** constant
        if (defined($const = "CURLOPT_" . strtoupper($opt))) {
            curl_setopt($this->handle, constant($const), $args[0]);
        }
        # curl_action function
        elseif (function_exists($func = "curl_$opt")) {
            return call_user_func_array($func, array_merge(array($this->handle), $args));
        }
        # neither exists
        else {
            trigger_error("curl: unknown '$opt' option", E_USER_ERROR);
        }
        
        return $this;
    }
    
    
    /**
     * Append result-checks such as ["HTTP_CODE" => [200, 201]].
     *
     */
    function assert($list) {
        $this->assert += $list;
        return $this;
    }


    /**
     * Wrap exec() to test result handle for HTTP or CURL properties.
     *
     */
    function exec() {
        $args = func_get_args();
        $content = $this->__call("exec", $args);
        foreach ($this->assert as $option => $values) {
            if (!in_array($this->$option, $values)) {
                return NULL;
            }
        }
        return $content;
    }

    
    /**
     * retrieve constants 
     *
     */
    function __get($opt) {
        // @implicit error message from constant() for non-existant symbols
        return curl_getinfo($this->handle, constant($const = "CURLINFO_" . strtoupper($opt)));
    }
    
    
}




# hybrid constructor
function curl_multi($url=NULL) {
    return new curl_multi($url);
}


/**
 * Associative handler for multiple concurrent curl requests.
 * Not so fluent.
 *
 */
class curl_multi {


    /**
     * Indexed or associative list
     *
     */
    public $list = array();

    
    /**
     * Multi-handle
     *
     */
    public $mh = NULL;


    /**
     * Bind list of curl handles.
     *
     * An associative array can be passed here, either of prepared curl() handles,
     * just URLs, or an curl option array each.
     *
     */
    function __construct($list) {
    
        // optionally get indexed params as list
        if (func_num_args() >= 2) {
           $list = func_get_args();
        }
        
        // auto-wrap curl() handlers
        $list = array_map("curl", $list);

        // copy handle list
        $this->list = $list;
        
        // bind actual curl handles
        $this->mh = curl_multi_init();
        foreach ($list as $cw) {
            curl_multi_add_handle($this->mh, $cw->handle);
        }
    }
    
    
    /**
     * Run until all individual curl() handles are finished.
     *
     * Implicitly returns a list of result contents (associative array as well).
     *
     */
    function exec($timeout=1.50, $t=0.0, $running=1) {

        // process within timeframe
        while (($timeout > $t += $timeout/50) and $running) {

            curl_multi_exec($this->mh, $running);
            curl_multi_select($this->mh, $timeout/50);
        }

        // fetch results, then close handles
        return $this->close( $this->getcontent() );
    }

    
    /**
     * Retrieve results, associative array.
     *
     */
    function getcontent() {
        return array_map("curl_multi_getcontent", array_map("current",/*retrieve first property (->handle) each*/ $this->list));
    }
    

    /**
     * Close all curl() handles in $list, and the curl-multi wrapper.
     *
     */
    function close($r=NULL) {
        $this->__call("close", array());
        curl_multi_close($this->mh);
        return $r;
    }


    /**
     * Applies options on each curl handle.
     *
     */
    function __call($opt, $args) {
        foreach ($this->list as $cw) {
            $cw->__call($opt, $args);
        }
    }

}


?>