【问题标题】:PHP, cURL, and HTTP POST example?PHP、cURL 和 HTTP POST 示例?
【发布时间】:2011-01-09 11:01:36
【问题描述】:

谁能告诉我如何使用 HTTP POST 执行 PHP cURL?

我想这样发送数据:

username=user1, password=passuser1, gender=1

www.example.com

我希望 cURL 返回类似 result=OK 的响应。有例子吗?

【问题讨论】:

    标签: php http curl http-post


    【解决方案1】:

    我很惊讶没有人建议 file_get_contents:

    $url = "http://www.example.com";
    $parameters = array('username' => 'user1', 'password' => 'passuser1', 'gender' => '1');
    $options = array('http' => array(
        'header'  => 'Content-Type: application/x-www-form-urlencoded\r\n',
        'method'  => 'POST',
        'content' => http_build_query($parameters)
    ));
    
    $context  = stream_context_create($options);
    $result = file_get_contents($url, false, $context);
    

    很简单,很有效;我在我控制两端代码的环境中使用它。

    更好的是,使用 json_decode(并设置您的代码以返回 JSON)

    $result = json_decode(file_get_contents($url, false, $context), TRUE);
    

    这种方法在幕后调用 curl,但您不会跳过那么多圈。

    从 Stack Overflow 其他地方的原始答案中提炼出来的答案: PHP sending variables to file_get_contents()

    【讨论】:

    • 这是一个非常好的答案。但是,它仅在我删除内容类型标题部分时才对我有用。
    • @lukas,内容类型标头是由 SO 编辑器添加的,而不是我...
    【解决方案2】:

    1.循序渐进

    • 初始化 cURL 会话:
    $url = "www.domain.com";
    $ch = curl_init($url);
    
    • 如果您的请求包含不记名令牌之类的标头或定义 JSON 内容,您必须将 HTTPHEADER 选项设置为 cURL:
    $token = "generated token code";
    curl_setopt(
        $ch, 
        CURLOPT_HTTPHEADER, 
        array(
            'Content-Type: application/json', // for define content type that is json
            'bearer: '.$token, // send token in header request
            'Content-length: 100' // content length for example 100 characters (can add by strlen($fields))
        )
    );
    
    • 如果要在输出集中包含标题集CURLOPT_HEADERtrue
    curl_setopt($ch, CURLOPT_HEADER, false);
    
    • RETURNTRANSFER 选项设置为true 以将传输作为字符串返回,而不是直接输出:
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    
    • 要检查SSL对等证书中是否存在公用名,可以设置为0(to not check the names)1(not supported in cURL 7.28.1)2(default value and for production mode)
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
    
    • 通过 cURL 将字段作为数组发布:
    $fields = array(
        "username" => "user1",
        "password" => "passuser1",
        "gender" => 1
    );
    curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
    
    • 执行 cURL 并返回字符串。根据您的资源,这会返回类似result=OK 的输出:
    $result = curl_exec($ch);
    
    • 关闭 cURL 资源,释放系统资源:
    curl_close($ch);
    

    2.作为类使用

    • 可以扩展的整个call_cURL类:
    class class_name_for_call_cURL {
        protected function getUrl() {
            return "www.domain.com";
        }
    
        public function call_cURL() {
            $token = "generated token code";
    
            $fields = array(
                "username" => "user1",
                "password" => "passuser1",
                "gender" => 1
            );
    
            $url = $this->getUrl();
            $output = $this->_execute($fields, $url, $token);
            
            // if you want to get json data
            // $output = json_decode($output);
                
            if ($output == "OK") {
                return true;
            } else {
                 return false;
            }
        }
    
        private function _execute($postData, $url, $token) {
            // for sending data as json type
            $fields = json_encode($postData);
    
            $ch = curl_init($url);
            curl_setopt(
                $ch, 
                CURLOPT_HTTPHEADER, 
                array(
                    'Content-Type: application/json', // if the content type is json
                    'bearer: '.$token // if you need token in header
                )
            );
            curl_setopt($ch, CURLOPT_HEADER, false);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
    
            $result = curl_exec($ch);
            curl_close($ch);
    
            return $result;
        }
    }
    
    • 使用类并调用 cURL:
    $class = new class_name_for_call_cURL();
    var_dump($class->call_cURL()); // output is true/false
    

    3.一个功能

    • 在任何需要的地方使用的功能:
    function get_cURL() {
    
            $url = "www.domain.com";
            $token = "generated token code";
    
            $postData = array(
                "username" => "user1",
                "password" => "passuser1",
                "gender" => 1
            );
    
            // for sending data as json type
            $fields = json_encode($postData);
    
            $ch = curl_init($url);
            curl_setopt(
                $ch, 
                CURLOPT_HTTPHEADER, 
                array(
                    'Content-Type: application/json', // if the content type is json
                    'bearer: '.$token // if you need token in header
                )
            );
            curl_setopt($ch, CURLOPT_HEADER, false);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
    
            $result = curl_exec($ch);
            curl_close($ch);
    
            return $result;
    }
    
    • 此功能仅适用于:
    var_dump(get_cURL());
    

    【讨论】:

      【解决方案3】:

      如果您尝试使用 cookie 登录网站。

      这段代码:

      if ($server_output == "OK") { ... } else { ... }
      

      如果您尝试登录可能会不起作用,因为许多网站返回状态200,但发布不成功。

      检查登录帖子是否成功的简单方法是检查它是否再次设置cookie。如果在输出中有一个 Set-Cookies 字符串,这意味着帖子不成功,它会开始一个新的会话。

      另外,发帖可以成功,但是状态可以重定向而不是200。

      要确保帖子成功,请尝试以下操作:

      在帖子之后跟随位置,因此它将转到帖子重定向到的页面:

      curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
      

      然后检查请求中是否存在新的 cookie:

      if (!preg_match('/^Set-Cookie:\s*([^;]*)/mi', $server_output)) 
      
      {echo 'post successful'; }
      
      else { echo 'not successful'; }
      

      【讨论】:

        【解决方案4】:

        程序

        // set post fields
        $post = [
            'username' => 'user1',
            'password' => 'passuser1',
            'gender'   => 1,
        ];
        
        $ch = curl_init('http://www.example.com');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
        
        // execute!
        $response = curl_exec($ch);
        
        // close the connection, release resources used
        curl_close($ch);
        
        // do anything you want with your response
        var_dump($response);
        

        面向对象

        <?php
        
        // mutatis mutandis
        namespace MyApp\Http;
        
        class CurlPost
        {
            private $url;
            private $options;
                   
            /**
             * @param string $url     Request URL
             * @param array  $options cURL options
             */
            public function __construct($url, array $options = [])
            {
                $this->url = $url;
                $this->options = $options;
            }
        
            /**
             * Get the response
             * @return string
             * @throws \RuntimeException On cURL error
             */
            public function __invoke(array $post)
            {
                $ch = \curl_init($this->url);
                
                foreach ($this->options as $key => $val) {
                    \curl_setopt($ch, $key, $val);
                }
        
                \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true);
                \curl_setopt($ch, \CURLOPT_POSTFIELDS, $post);
        
                $response = \curl_exec($ch);
                $error    = \curl_error($ch);
                $errno    = \curl_errno($ch);
                
                if (\is_resource($ch)) {
                    \curl_close($ch);
                }
        
                if (0 !== $errno) {
                    throw new \RuntimeException($error, $errno);
                }
                
                return $response;
            }
        }
        

        用法

        // create curl object
        $curl = new \MyApp\Http\CurlPost('http://www.example.com');
        
        try {
            // execute the request
            echo $curl([
                'username' => 'user1',
                'password' => 'passuser1',
                'gender'   => 1,
            ]);
        } catch (\RuntimeException $ex) {
            // catch errors
            die(sprintf('Http error %s with code %d', $ex->getMessage(), $ex->getCode()));
        }
        

        这里的旁注:最好创建某种名为AdapterInterface 的接口,例如使用getResponse() 方法并让上面的类实现它。然后,您始终可以将此实现与您喜欢的另一个适配器交换,而不会对您的应用程序产生任何副作用。

        使用 HTTPS / 加密流量

        通常在Windows操作系统下PHP中的cURL有问题。在尝试连接到受 https 保护的端点时,您将收到一条错误消息,告诉您 certificate verify failed

        大多数人在这里所做的就是告诉 cURL 库简单地忽略证书错误并继续 (curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);)。由于这将使您的代码正常工作,因此您会引入巨大的安全漏洞并使恶意用户能够对您的应用执行各种攻击,例如Man In The Middle 攻击等。

        永远不要那样做。相反,您只需修改您的 php.ini 并告诉 PHP 您的 CA Certificate 文件在哪里,让它正确验证证书:

        ; modify the absolute path to the cacert.pem file
        curl.cainfo=c:\php\cacert.pem
        

        最新的cacert.pem可以从网上或者extracted from your favorite browser下载。更改任何php.ini 相关设置时,请记住重新启动您的网络服务器。

        【讨论】:

        • 这应该是公认的答案,因为最佳实践是让 HTTP 库处理变量的编码。
        • 并非总是如此。我见过期望 POST 变量以某种方式编码的 Web 服务器,否则会导致它们失败。在我看来,http_build_query() 实际上比 cURL 更可靠。
        • HTTP 规范对 POST 参数的外观非常简单。无论如何,网络服务器软件都应该符合标准。
        • 通过使用这种方式,您将强制 cURL 使用稍微不同类型的 POST。 (预期:100-继续)。查看这篇文章:support.urbanairship.com/entries/…
        • 扩展 @César 的评论,PHP documentation 明确指出以下内容:“将数组传递给 CURLOPT_POSTFIELDS 会将数据编码为 multipart/form-data,同时传递URL 编码字符串会将数据编码为 application/x-www-form-urlencoded."。我最近花费了大量时间来尝试排除 cURL 调用在第三方端点上失败的原因,最终才意识到他们不支持 multipart/form-data。
        【解决方案5】:

        发送formraw数据示例:

        $curlHandler = curl_init();
        
        curl_setopt_array($curlHandler, [
            CURLOPT_URL => 'https://postman-echo.com/post',
            CURLOPT_RETURNTRANSFER => true,
        
            /**
             * Specify POST method
             */
            CURLOPT_POST => true,
        
            /**
             * Specify array of form fields
             */
            CURLOPT_POSTFIELDS => [
                'foo' => 'bar',
                'baz' => 'biz',
            ],
        ]);
        
        $response = curl_exec($curlHandler);
        
        curl_close($curlHandler);
        
        echo($response);
        

        【讨论】:

          【解决方案6】:
          curlPost('google.com', [
              'username' => 'admin',
              'password' => '12345',
          ]);
          
          
          function curlPost($url, $data) {
              $ch = curl_init($url);
              curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
              curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
              $response = curl_exec($ch);
              $error = curl_error($ch);
              curl_close($ch);
              if ($error !== '') {
                  throw new \Exception($error);
              }
          
              return $response;
          }
          

          【讨论】:

          • 您的代码不会关闭句柄并释放资源,因为您在抛出异常后 curl_close。您应该在 finally 块内使用 curl_close
          【解决方案7】:
          <?php
          //
          // A very simple PHP example that sends a HTTP POST to a remote site
          //
          
          $ch = curl_init();
          
          curl_setopt($ch, CURLOPT_URL,"http://www.example.com/tester.phtml");
          curl_setopt($ch, CURLOPT_POST, 1);
          curl_setopt($ch, CURLOPT_POSTFIELDS,
                      "postvar1=value1&postvar2=value2&postvar3=value3");
          
          // In real life you should use something like:
          // curl_setopt($ch, CURLOPT_POSTFIELDS, 
          //          http_build_query(array('postvar1' => 'value1')));
          
          // Receive server response ...
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
          
          $server_output = curl_exec($ch);
          
          curl_close ($ch);
          
          // Further processing ...
          if ($server_output == "OK") { ... } else { ... }
          ?>
          

          【讨论】:

          • 无需使用http_build_query()处理参数;只需将数组传递给CURLOPT_POSTFIELDS 就足够了。
          • @Raptor 直接向 CURLOPT_POSTFIELDS 提供数组实际上 curl 使 POST 类型略有不同。 (预计:100-继续)
          • 另外,如果CURLOPT_POSTFIELDS 的值是一个数组,则Content-Type 标头将设置为multipart/form-data 而不是application/x-www-form-urlencodedphp.net/manual/en/function.curl-setopt.php
          • 使用 CURLOPT_RETURNTRANSFER 意味着 curl_exec 会将响应作为字符串返回而不是输出。
          • 我建议使用true 而不是1 代替CURLOPT_POST
          【解决方案8】:

          如果表单使用重定向、身份验证、cookie、SSL (https) 或其他任何东西,而不是期望 POST 变量的完全开放的脚本,那么您将很快开始咬牙切齿。看看Snoopy,它完全符合您的想法,同时无需设置大量开销。

          【讨论】:

          • 如果你想坚持使用股票库,只需尝试添加curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
          • 唯一的缺点是您仍然需要处理设置 cookie jar 和其他潜在问题(例如是否遵循重定向、如何处理非基于 HTTP 的身份验证等)。 6 年后,我会推荐更通用的“无头浏览器”概念,而不是那个特定的库(或者 sourceforge 上的任何东西,过时了,对吧?)虽然我通常只是直接处理 curl 选项,但我仍然建议查看与PSR-7 兼容的无头浏览器库(Guzzle 是我唯一知道的一个)以避免头痛。
          【解决方案9】:

          Curl Post + 错误处理 + 设置标题 [感谢@mantas-d]:

          function curlPost($url, $data=NULL, $headers = NULL) {
              $ch = curl_init($url);
              curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
          
              if(!empty($data)){
                  curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
              }
          
              if (!empty($headers)) {
                  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
              }
          
              $response = curl_exec($ch);
          
              if (curl_error($ch)) {
                  trigger_error('Curl Error:' . curl_error($ch));
              }
          
              curl_close($ch);
              return $response;
          }
          
          
          curlPost('google.com', [
              'username' => 'admin',
              'password' => '12345',
          ]);
          

          【讨论】:

          • 您的代码不会关闭句柄并释放资源,因为您在抛出异常后 curl_close。你应该 curl_close 在 finally 块内。
          【解决方案10】:

          可以通过以下方式轻松到达:

          <?php
          
          $post = [
              'username' => 'user1',
              'password' => 'passuser1',
              'gender'   => 1,
          ];
          $ch = curl_init();
          curl_setopt($ch, CURLOPT_URL, 'http://www.domain.com');
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
          curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
          $response = curl_exec($ch);
          var_export($response);
          

          【讨论】:

            【解决方案11】:

            使用 php curl_exec 进行 HTTP 发布的实例:

            把它放在一个名为 foobar.php 的文件中:

            <?php
              $ch = curl_init();
              $skipper = "luxury assault recreational vehicle";
              $fields = array( 'penguins'=>$skipper, 'bestpony'=>'rainbowdash');
              $postvars = '';
              foreach($fields as $key=>$value) {
                $postvars .= $key . "=" . $value . "&";
              }
              $url = "http://www.google.com";
              curl_setopt($ch,CURLOPT_URL,$url);
              curl_setopt($ch,CURLOPT_POST, 1);                //0 for a get request
              curl_setopt($ch,CURLOPT_POSTFIELDS,$postvars);
              curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
              curl_setopt($ch,CURLOPT_CONNECTTIMEOUT ,3);
              curl_setopt($ch,CURLOPT_TIMEOUT, 20);
              $response = curl_exec($ch);
              print "curl response is:" . $response;
              curl_close ($ch);
            ?>
            

            然后使用命令php foobar.php 运行它,它将这种输出转储到屏幕:

            <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 
            "http://www.w3.org/TR/html4/strict.dtd">
            <html>
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            <title>Title</title>
            
            <meta http-equiv="Pragma" content="no-cache">
            <meta http-equiv="Expires" content="0">
            <body>
              A mountain of content...
            </body>
            </html>
            

            所以你向 www.google.com 做了一个 PHP POST 并向它发送了一些数据。

            如果服务器被编程为读取 post 变量,它可以根据它决定做一些不同的事情。

            【讨论】:

            • $postvars .= $key . $value; 是否应该$postvars .= $key . $value ."&amp;";
            • 再看这个答案,你也可以用http_build_query替换你的自定义查询字符串转换器实现,只要给它$fields数组,它就会输出一个查询字符串。
            • 请注意,您应该对数据进行编码以便安全提交。
            • 哦不,不要尝试自己构建帖子字符串!使用这个:curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields));
            • -1 因为您没有转义您的帖子变量。 OP 的示例是发送用户提交的用户名和密码以进行身份​​验证。使用您的解决方案,密码中带有 & 的用户将永远无法登录。oriadam 的评论是正确的,但您可以省略 http_build_query,例如:curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
            【解决方案12】:

            这里有一些 PHP + curl 的样板代码 http://www.webbotsspidersscreenscrapers.com/DSP_download.php

            包含在这些库中将简化开发

            <?php
            # Initialization
            include("LIB_http.php");
            include("LIB_parse.php");
            $product_array=array();
            $product_count=0;
            
            # Download the target (store) web page
            $target = "http://www.tellmewhenitchanges.com/buyair";
            $web_page = http_get($target, "");
                ...
            ?>
            

            【讨论】:

              【解决方案13】:

              如果您将信息传递到自己的网站,一个更简单的答案是使用 SESSION 变量。开始 php 页面:

              session_start();
              

              如果在某些时候你想在 PHP 中生成信息并传递到会话的下一页,而不是使用 POST 变量,将其分配给 SESSION 变量。示例:

              $_SESSION['message']='www.'.$_GET['school'].'.edu was not found.  Please try again.'
              

              然后在下一页您只需引用此 SESSION 变量。注意:使用后请务必销毁,以免在使用后持续存在:

              if (isset($_SESSION['message'])) {echo $_SESSION['message']; unset($_SESSION['message']);}
              

              【讨论】:

                猜你喜欢
                • 2013-02-24
                • 1970-01-01
                • 2018-06-13
                • 1970-01-01
                • 2012-02-23
                • 2015-05-13
                相关资源
                最近更新 更多