I am trying to study class.openid.php because it is simpler and smaller than
lightopenid. for my purposes 200 lines do matter. But class.openid.php does not work with google openID https://www.google.com/accounts/o8/id, prints to me such error:
ERROR CODE: OPENID_NOSERVERSFOUND
ERROR DESCRIPTION: Cannot find OpenID Server TAG on Identity page.
is it possible to make class.openid.php (any version) work with google openID and how to do such thing?
class.openid.php can be taken here but it did not worked for me out of the box so I had to find all <? and replace tham with <?php in case someone would like to see code I've got:
html interface page:
<?php
require('class.openid.v3.php');
if ($_POST['openid_action'] == "login"){ // Get identity from user and redirect browser to OpenID Server
$openid = new SimpleOpenID;
$openid->SetIdentity($_POST['openid_url']);
$openid->SetTrustRoot('http://' . $_SERVER["HTTP_HOST"]);
$openid->SetRequiredFields(array('email','fullname'));
$openid->SetOptionalFields(array('dob','gender','postcode','country','language','timezone'));
if ($openid->GetOpenIDServer()){
$openid->SetApprovedURL('http://' . $_SERVER["HTTP_HOST"] . $_SERVER["PATH_INFO"]); // Send Response from OpenID server to this script
$openid->Redirect(); // This will redirect user to OpenID Server
}else{
$error = $openid->GetError();
echo "ERROR CODE: " . $error['code'] . "<br>";
echo "ERROR DESCRIPTION: " . $error['description'] . "<br>";
}
exit;
}
else if($_GET['openid_mode'] == 'id_res'){ // Perform HTTP Request to OpenID server to validate key
$openid = new SimpleOpenID;
$openid->SetIdentity($_GET['openid_identity']);
$openid_validation_result = $openid->ValidateWithServer();
if ($openid_validation_result == true){ // OK HERE KEY IS VALID
echo "VALID";
}else if($openid->IsError() == true){ // ON THE WAY, WE GOT SOME ERROR
$error = $openid->GetError();
echo "ERROR CODE: " . $error['code'] . "<br>";
echo "ERROR DESCRIPTION: " . $error['description'] . "<br>";
}else{ // Signature Verification Failed
echo "INVALID AUTHORIZATION";
}
}else if ($_GET['openid_mode'] == 'cancel'){ // User Canceled your Request
echo "USER CANCELED REQUEST";
}
?>
<html>
<head>
<title>OpenID Example</title>
</head>
<body>
<div>
<fieldset id="openid">
<legend>OpenID Login</legend>
<form action="<?php echo 'http://' . $_SERVER["HTTP_HOST"] . $_SERVER["PATH_INFO"]; ?>" method="post" onsubmit="this.login.disabled=true;">
<input type="hidden" name="openid_action" value="login">
<div><input type="text" name="openid_url" class="openid_login"><input type="submit" name="login" value="login >>"></div>
<div><a href="http://www.myopenid.com/" class="link" >Get an OpenID</a></div>
</form>
</fieldset>
</div>
<div style="margin-top: 2em; font-family: arial; font-size: 0.8em; border-top:1px solid gray; padding: 4px;">Sponsored by: FiveStores - get your free online store; includes extensive API for developers; <i style="color: gray;">integrated with OpenID</i></div>
</body>
</html>
and php class
<?php
/*
FREE TO USE Under License: GPLv3
Simple OpenID PHP Class
Some modifications by Eddie Roosenmaallen, eddie#roosenmaallen.com
*/
class SimpleOpenID{
var $openid_url_identity;
var $URLs = array();
var $error = array();
var $fields = array(
'required' => array(),
'optional' => array(),
);
function SimpleOpenID(){
if (!function_exists('curl_exec')) {
die('Error: Class SimpleOpenID requires curl extension to work');
}
}
function SetOpenIDServer($a){
$this->URLs['openid_server'] = $a;
}
function SetTrustRoot($a){
$this->URLs['trust_root'] = $a;
}
function SetCancelURL($a){
$this->URLs['cancel'] = $a;
}
function SetApprovedURL($a){
$this->URLs['approved'] = $a;
}
function SetRequiredFields($a){
if (is_array($a)){
$this->fields['required'] = $a;
}else{
$this->fields['required'][] = $a;
}
}
function SetOptionalFields($a){
if (is_array($a)){
$this->fields['optional'] = $a;
}else{
$this->fields['optional'][] = $a;
}
}
function SetIdentity($a){ // Set Identity URL
if ((stripos($a, 'http://') === false)
&& (stripos($a, 'https://') === false)){
$a = 'http://'.$a;
}
$this->openid_url_identity = $a;
}
function GetIdentity(){ // Get Identity
return $this->openid_url_identity;
}
function GetError(){
$e = $this->error;
return array('code'=>$e[0],'description'=>$e[1]);
}
function ErrorStore($code, $desc = null){
$errs['OPENID_NOSERVERSFOUND'] = 'Cannot find OpenID Server TAG on Identity page.';
if ($desc == null){
$desc = $errs[$code];
}
$this->error = array($code,$desc);
}
function IsError(){
if (count($this->error) > 0){
return true;
}else{
return false;
}
}
function splitResponse($response) {
$r = array();
$response = explode("\n", $response);
foreach($response as $line) {
$line = trim($line);
if ($line != "") {
list($key, $value) = explode(":", $line, 2);
$r[trim($key)] = trim($value);
}
}
return $r;
}
function OpenID_Standarize($openid_identity = null){
if ($openid_identity === null)
$openid_identity = $this->openid_url_identity;
$u = parse_url(strtolower(trim($openid_identity)));
if (!isset($u['path']) || ($u['path'] == '/')) {
$u['path'] = '';
}
if(substr($u['path'],-1,1) == '/'){
$u['path'] = substr($u['path'], 0, strlen($u['path'])-1);
}
if (isset($u['query'])){ // If there is a query string, then use identity as is
return $u['host'] . $u['path'] . '?' . $u['query'];
}else{
return $u['host'] . $u['path'];
}
}
function array2url($arr){ // converts associated array to URL Query String
if (!is_array($arr)){
return false;
}
$query = '';
foreach($arr as $key => $value){
$query .= $key . "=" . $value . "&";
}
return $query;
}
function CURL_Request($url, $method="GET", $params = "") { // Remember, SSL MUST BE SUPPORTED
if (is_array($params)) $params = $this->array2url($params);
$curl = curl_init($url . ($method == "GET" && $params != "" ? "?" . $params : ""));
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_HTTPGET, ($method == "GET"));
curl_setopt($curl, CURLOPT_POST, ($method == "POST"));
if ($method == "POST") curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
if (curl_errno($curl) == 0){
$response;
}else{
$this->ErrorStore('OPENID_CURL', curl_error($curl));
}
return $response;
}
function HTML2OpenIDServer($content) {
$get = array();
// Get details of their OpenID server and (optional) delegate
preg_match_all('/<link[^>]*rel=[\'"]openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"]openid.server[\'"][^>]*\/?>/i', $content, $matches2);
$servers = array_merge($matches1[1], $matches2[1]);
preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2);
$delegates = array_merge($matches1[1], $matches2[1]);
$ret = array($servers, $delegates);
return $ret;
}
function GetOpenIDServer(){
$response = $this->CURL_Request($this->openid_url_identity);
list($servers, $delegates) = $this->HTML2OpenIDServer($response);
if (count($servers) == 0){
$this->ErrorStore('OPENID_NOSERVERSFOUND');
return false;
}
if (isset($delegates[0])
&& ($delegates[0] != "")){
$this->SetIdentity($delegates[0]);
}
$this->SetOpenIDServer($servers[0]);
return $servers[0];
}
function GetRedirectURL(){
$params = array();
$params['openid.return_to'] = urlencode($this->URLs['approved']);
$params['openid.mode'] = 'checkid_setup';
$params['openid.identity'] = urlencode($this->openid_url_identity);
$params['openid.trust_root'] = urlencode($this->URLs['trust_root']);
if (isset($this->fields['required'])
&& (count($this->fields['required']) > 0)) {
$params['openid.sreg.required'] = implode(',',$this->fields['required']);
}
if (isset($this->fields['optional'])
&& (count($this->fields['optional']) > 0)) {
$params['openid.sreg.optional'] = implode(',',$this->fields['optional']);
}
return $this->URLs['openid_server'] . "?". $this->array2url($params);
}
function Redirect(){
$redirect_to = $this->GetRedirectURL();
if (headers_sent()){ // Use JavaScript to redirect if content has been previously sent (not recommended, but safe)
echo '<script language="JavaScript" type="text/javascript">window.location=\'';
echo $redirect_to;
echo '\';</script>';
}else{ // Default Header Redirect
header('Location: ' . $redirect_to);
}
}
function ValidateWithServer(){
$params = array(
'openid.assoc_handle' => urlencode($_GET['openid_assoc_handle']),
'openid.signed' => urlencode($_GET['openid_signed']),
'openid.sig' => urlencode($_GET['openid_sig'])
);
// Send only required parameters to confirm validity
$arr_signed = explode(",",str_replace('sreg.','sreg_',$_GET['openid_signed']));
for ($i=0; $i<count($arr_signed); $i++){
$s = str_replace('sreg_','sreg.', $arr_signed[$i]);
$c = $_GET['openid_' . $arr_signed[$i]];
// if ($c != ""){
$params['openid.' . $s] = urlencode($c);
// }
}
$params['openid.mode'] = "check_authentication";
$openid_server = $this->GetOpenIDServer();
if ($openid_server == false){
return false;
}
$response = $this->CURL_Request($openid_server,'POST',$params);
$data = $this->splitResponse($response);
if ($data['is_valid'] == "true") {
return true;
}else{
return false;
}
}
}
?>
The problem is that Google doesn't just supply an OpenID endpoint.
OpenId endpoints include an identifier for the user.
What we are having here is called a Discovery Url.
This is a static url that you can direct any user to, and the service itself will recognise the user and return a per-user unique identifying url.
This however is NOT implemented correctly by most openid client libraries, including the majority linked on the official openid website.
Even the Zend Framework libraries are incapable of handling that.
However I found a class that I analysed from various perspectives and that I am very satisfied with. At the company I work at we already integrated it successfully in several production environments and have not experienced any problems.
You may also be interested in another post of mine dealing with the issue of making Facebook an openid Provider. The class I am using, that also supports Google, can also be found there:
Best way to implement Single-Sign-On with all major providers?
The class in your question does not support OpenID 2.0 at all. Therefore, it will not work with Google without adding a lot of code.
Are you searching something like :
http://wiki.openid.net/w/page/12995176/Libraries
?
There is a PHP section in that.
Related
Can you please help me to integrate paynow zimbabwe gateway with my localhost system.I have tried to follow their documentation https://developers.paynow.co.zw/docs/quickstart.html but I failed. I want the user to be redirected to the paynow page to pay penalties.Also the result or status must be obtained in order to update the system database. Is it possible to link a localhost system to the paynow api or my system have to be live?. Thank you in advance
<?php
include "./includes/tables_header.php";
include "./includes/db.php";
require_once "./paynow/autoloader.php";
use Paynow\Payments\Paynow;
if(isset($_POST['Paynow']))
{
class Payow{
public function paynows($amount)
{
$siteurl="http://localhost/online_offenceTracking_system/payment1.php?";//substitute with your own return url
define('ps_error', 'Error');
define('ps_ok','Ok');
define('ps_created_but_not_paid','created but not paid');
define('ps_cancelled','cancelled');
define('ps_failed','failed');
define('ps_paid','paid');
define('ps_awaiting_delivery','awaiting delivery');
define('ps_delivered','delivered');
define('ps_awaiting_redirect','awaiting redirect');
define('site_url', $siteurl);
$int_key="###########";//get from paynow.co.zw
$int_id=#######;//get from paynow.co.zw, it should be an intenger
$paymentid="testID1234hs";
$url="https://www.paynow.co.zw/interface/initiatetransaction/?";
$reference=sha1(Paynow\Payments\Paynow::$app->user->identity->email);
$amount=6.25;
$returnurl="http://localhost/online_offenceTracking_system/payment1.php?r=credit/index"; //substitute with your own return urls
$resulturl="http://localhost/online_offenceTracking_system/payment1.php?r=credit/index"; //substitute with your own return urls
$authemail="acmwamuka#gmail.com";//This is the buyer's email address
$additionalinfo="Paying for canteen meals.";
$concat=$int_key.$int_id.$paymentid.$url.$reference.$returnurl.$resulturl.$authemail.$additionalinfo;
$concat=$concat.$int_key;
$values = array('resulturl' => $resulturl,
'returnurl' => $returnurl,
'reference' => $reference,
'amount' => $amount,
'id' => $int_id,
'additionalinfo' => $additionalinfo,
'authemail' => $authemail,
'authphone' => "07777777777",
'status' => 'Message'); //just a simple message
$fields_string = $this->CreateMsg($values,$int_key);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); //need fixing
$result = curl_exec($ch);
if($result)
{
$msg = $this->ParseMsg($result);
if ($msg["status"] == ps_error){
header("Location: $checkout_url");
exit;
}
else if ($msg["status"] == "Ok"){
$validateHash = $this->CreateHash($msg, $int_key);
if($validateHash != $msg["hash"]){
$error = "Paynow reply hashes do not match : " . $validateHash . " - " . $msg["hash"];
echo $error;
}
else
{
$theProcessUrl = $msg["browserurl"];
//echo $theProcessUrl;
//header("Location: ".$theProcessUrl);
Paynow\Payments\Paynow::$app->response->redirect($theProcessUrl);
$orders_array = array();
}
}
else {
//unknown status or one you dont want to handle locally
$error = "Invalid status from Paynow, cannot continue.";
}
}
else
{
$error = curl_error($ch);
echo $error;
}
//print_r($result);
//close connection
curl_close($ch);
}
public function ParseMsg($msg) {
$parts = explode("&",$msg);
$result = array();
foreach($parts as $i => $value) {
$bits = explode("=", $value, 2);
$result[$bits[0]] = urldecode($bits[1]);
}
return $result;
}
function CreateMsg($values, $MerchantKey){
$fields = array();
foreach($values as $key=>$value) {
$fields[$key] = urlencode($value);
}
$fields["hash"] = urlencode($this->CreateHash($values, $MerchantKey));
$fields_string = $this->UrlIfy($fields);
return $fields_string;
}
public function UrlIfy($fields) {
$delim = "";
$fields_string = "";
foreach($fields as $key=>$value) {
$fields_string .= $delim . $key . '=' . $value;
$delim = "&";
}
return $fields_string;
}
public function CreateHash($values, $MerchantKey){
$string = "";
foreach($values as $key=>$value) {
if( strtoupper($key) != "HASH" ){
$string .= $value;
}
}
$string .= $MerchantKey;
$hash = hash("sha512", $string);
return strtoupper($hash);
}
}}
?>
You can use a free service like ngrok to expose your localhost environment to the world wide web. Just make sure your returnurl and resulturl are using your ngrok address so that Paynow can callback your application.
In summary: Stripe API: CurlClient.php has a CURLOPT_HEADERFUNCTION that cannot be found by curl on execution of the curl_exec command.
Stripe API version 3.2.0 (the newer version that uses namespace) using PHP version 5.4.45 and CURL version 7.36.0. I downloaded the library from GitHub and am using the init.php routine provided with the Library to bind the Stripe classes.
I can get a token using Stripe.js, pass to my server, authenticate and post a charge:
\Stripe\Charge::create(array(
"amount" => 400,
"currency" => "cad",
"source" => "tok_74MBD61UgMTtN7", // obtained with Stripe.js
"description" => "Charge for test#example.com"
), array(
"idempotency_key" => "yfwO6fUZh4qHctt6",
));
I get an error: Unexpected error communicating with Stripe. If this problem persists, let us know at support#stripe.com. (I have contacted Stripe and haven't heard from them in 3 days.)
My error routine shows: Invalid callback , no array or string given file (...)stripe-php-3.2.0/lib/HttpClient/CurlClient.php. Investigation shows the curl_exec in CurlClient.php throws the error because it can't find $headerCallback. If I comment out this line:
$opts[CURLOPT_HEADERFUNCTION] = $headerCallback;
then everything works fine, except of course I don't get any headers passed back to the calling functions. However, I do get json returned and the successful charge can be seen from the Stripe Control Panel.
So, any ideas on why Strip API won't function properly?
Posting CurlClient.php here:
<?php
namespace Stripe\HttpClient;
use Stripe\Stripe;
use Stripe\Error;
use Stripe\Util;
class CurlClient implements ClientInterface
{
private static $instance;
public static function instance()
{
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function request($method, $absUrl, $headers, $params, $hasFile)
{
$curl = curl_init();
$method = strtolower($method);
$opts = array();
if ($method == 'get') {
if ($hasFile) {
throw new Error\Api(
"Issuing a GET request with a file parameter"
);
}
$opts[CURLOPT_HTTPGET] = 1;
if (count($params) > 0) {
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
}
} elseif ($method == 'post') {
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = $hasFile ? $params : self::encode($params);
} elseif ($method == 'delete') {
$opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
if (count($params) > 0) {
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
}
} else {
throw new Error\Api("Unrecognized method $method");
}
// Create a callback to capture HTTP headers for the response
$rheaders = array();
$headerCallback = function ($curl, $header_line) use (&$rheaders) {
// Ignore the HTTP request line (HTTP/1.1 200 OK)
if (strpos($header_line, ":") === false) {
return strlen($header_line);
}
list($key, $value) = explode(":", trim($header_line), 2);
$rheaders[trim($key)] = trim($value);
return strlen($header_line);
};
$absUrl = Util\Util::utf8($absUrl);
$opts[CURLOPT_URL] = $absUrl;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_CONNECTTIMEOUT] = 30;
$opts[CURLOPT_TIMEOUT] = 80;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_HEADERFUNCTION] = $headerCallback;
$opts[CURLOPT_HTTPHEADER] = $headers;
if (!Stripe::$verifySslCerts) {
$opts[CURLOPT_SSL_VERIFYPEER] = false;
}
curl_setopt_array($curl, $opts);
$rbody = curl_exec($curl);
if (!defined('CURLE_SSL_CACERT_BADFILE')) {
define('CURLE_SSL_CACERT_BADFILE', 77); // constant not defined in PHP
}
$errno = curl_errno($curl);
if ($errno == CURLE_SSL_CACERT ||
$errno == CURLE_SSL_PEER_CERTIFICATE ||
$errno == CURLE_SSL_CACERT_BADFILE
) {
array_push(
$headers,
'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}'
);
$cert = self::caBundle();
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_CAINFO, $cert);
$rbody = curl_exec($curl);
}
if ($rbody === false) {
$errno = curl_errno($curl);
$message = curl_error($curl);
curl_close($curl);
$this->handleCurlError($absUrl, $errno, $message);
}
$rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
return array($rbody, $rcode, $rheaders);
}
/**
* #param number $errno
* #param string $message
* #throws Error\ApiConnection
*/
private function handleCurlError($url, $errno, $message)
{
switch ($errno) {
case CURLE_COULDNT_CONNECT:
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_OPERATION_TIMEOUTED:
$msg = "Could not connect to Stripe ($url). Please check your "
. "internet connection and try again. If this problem persists, "
. "you should check Stripe's service status at "
. "https://twitter.com/stripestatus, or";
break;
case CURLE_SSL_CACERT:
case CURLE_SSL_PEER_CERTIFICATE:
$msg = "Could not verify Stripe's SSL certificate. Please make sure "
. "that your network is not intercepting certificates. "
. "(Try going to $url in your browser.) "
. "If this problem persists,";
break;
default:
$msg = "Unexpected error communicating with Stripe. "
. "If this problem persists,";
}
$msg .= " let us know at support#stripe.com.";
$msg .= "\n\n(Network error [errno $errno]: $message)";
throw new Error\ApiConnection($msg);
}
private static function caBundle()
{
return dirname(__FILE__) . '/../../data/ca-certificates.crt';
}
/**
* #param array $arr An map of param keys to values.
* #param string|null $prefix
*
* Only public for testability, should not be called outside of CurlClient
*
* #return string A querystring, essentially.
*/
public static function encode($arr, $prefix = null)
{
if (!is_array($arr)) {
return $arr;
}
$r = array();
foreach ($arr as $k => $v) {
if (is_null($v)) {
continue;
}
if ($prefix && $k && !is_int($k)) {
$k = $prefix."[".$k."]";
} elseif ($prefix) {
$k = $prefix."[]";
}
if (is_array($v)) {
$enc = self::encode($v, $k);
if ($enc) {
$r[] = $enc;
}
} else {
$r[] = urlencode($k)."=".urlencode($v);
}
}
return implode("&", $r);
}
}
The problem turned out to be environmental. My PHP (5.4.45) was installed using CageFS and I used the 5.4 version that was NOT native, allowing for selection of various PHP modules. When I switched to native version of PHP 5.4 then stripe API started working just fine. Also works well in PHP 5.5. This may be of interest to other Cloud Linux users working with CageFS and PHP Selector.
I'm using a PHP script (using cURL) to check whether:
The links in my database are correct (ie return HTTP status 200)
The links are in fact redirected and redirect to an appropriate/similar page (using the contents of the page )
The results of this are saved to a log file and emailed to me as an attachment.
This is all fine and working, however it is slow as all hell and half the time it times out and aborts itself early. Of note, I have about 16,000 links to check.
Was wondering how best to make this run quicker, and what I'm doing wrong?
Code below:
function echoappend ($file,$tobewritten) {
fwrite($file,$tobewritten);
echo $tobewritten;
}
error_reporting(E_ALL);
ini_set('display_errors', '1');
$filename=date('YmdHis') . "linkcheck.htm";
echo $filename;
$file = fopen($filename,"w+");
try {
$conn = new PDO('mysql:host=localhost;dbname=databasename',$un,$pw);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo '<b>connected to db</b><br /><br />';
$sitearray = array("medical.posterous","ebm.posterous","behavenet","guidance.nice","www.rch","emedicine","www.chw","www.rxlist","www.cks.nhs.uk");
foreach ($sitearray as $key => $value) {
$site=$value;
echoappend ($file, "<h1>" . $site . "</h1>");
$q="SELECT * FROM link WHERE url LIKE :site";
$stmt = $conn->prepare($q);
$stmt->execute(array(':site' => 'http://' . $site . '%'));
$result = $stmt->fetchAll();
$totallinks = 0;
$workinglinks = 0;
foreach($result as $row)
{
$ch = curl_init();
$originalurl = $row['url'];
curl_setopt($ch, CURLOPT_URL, $originalurl);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
$output = curl_exec($ch);
if ($output === FALSE) {
echo "cURL Error: " . curl_error($ch);
}
$urlinfo = curl_getinfo($ch);
if ($urlinfo['http_code'] == 200)
{
echoappend($file, $row['name'] . ": <b>working!</b><br />");
$workinglinks++;
}
else if ($urlinfo['http_code'] == 301 || 302)
{
$redirectch = curl_init();
curl_setopt($redirectch, CURLOPT_URL, $originalurl);
curl_setopt($redirectch, CURLOPT_HEADER, 1);
curl_setopt($redirectch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($redirectch, CURLOPT_NOBODY, false);
curl_setopt($redirectch, CURLOPT_FOLLOWLOCATION, true);
$redirectoutput = curl_exec($redirectch);
$doc = new DOMDocument();
#$doc->loadHTML($redirectoutput);
$nodes = $doc->getElementsByTagName('title');
$title = $nodes->item(0)->nodeValue;
echoappend ($file, $row['name'] . ": <b>redirect ... </b>" . $title . " ... ");
if (strpos(strtolower($title),strtolower($row['name']))===false) {
echoappend ($file, "FAIL<br />");
}
else {
$header = curl_getinfo($redirectch);
echoappend ($file, $header['url']);
echoappend ($file, "SUCCESS<br />");
}
curl_close($redirectch);
}
else
{
echoappend ($file, $row['name'] . ": <b>FAIL code</b>" . $urlinfo['http_code'] . "<br />");
}
curl_close($ch);
$totallinks++;
}
echoappend ($file, '<br />');
echoappend ($file, $site . ": " . $workinglinks . "/" . $totallinks . " links working. <br /><br />");
}
$conn = null;
echo '<br /><b>connection closed</b><br /><br />';
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
Short answer is use the curl_multi_* methods to parallelize your requests.
The reason for the slowness is that web requests are comparatively slow. Sometimes VERY slow. Using the curl_multi_* functions lets you run multiple requests simultaneously.
One thing to be careful about is to limit the number of requests you run at once. In other words, don't run 16,000 requests at once. Maybe start at 16 and see how that goes.
The following example should help you get started:
<?php
//
// Fetch a bunch of URLs in parallel. Returns an array of results indexed
// by URL.
//
function fetch_urls($urls, $curl_options = array()) {
$curl_multi = curl_multi_init();
$handles = array();
$options = $curl_options + array(
CURLOPT_HEADER => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOBODY => true,
CURLOPT_FOLLOWLOCATION => true);
foreach($urls as $url) {
$handles[$url] = curl_init($url);
curl_setopt_array($handles[$url], $options);
curl_multi_add_handle($curl_multi, $handles[$url]);
}
$active = null;
do {
$status = curl_multi_exec($curl_multi, $active);
} while ($status == CURLM_CALL_MULTI_PERFORM);
while ($active && ($status == CURLM_OK)) {
if (curl_multi_select($curl_multi) != -1) {
do {
$status = curl_multi_exec($curl_multi, $active);
} while ($status == CURLM_CALL_MULTI_PERFORM);
}
}
if ($status != CURLM_OK) {
trigger_error("Curl multi read error $status\n", E_USER_WARNING);
}
$results = array();
foreach($handles as $url => $handle) {
$results[$url] = curl_getinfo($handle);
curl_multi_remove_handle($curl_multi, $handle);
curl_close($handle);
}
curl_multi_close($curl_multi);
return $results;
}
//
// The urls to test
//
$urls = array("http://google.com", "http://yahoo.com", "http://google.com/probably-bogus", "http://www.google.com.au");
//
// The number of URLs to test simultaneously
//
$request_limit = 2;
//
// Test URLs in batches
//
$redirected_urls = array();
for ($i = 0 ; $i < count($urls) ; $i += $request_limit) {
$results = fetch_urls(array_slice($urls, $i, $request_limit));
foreach($results as $url => $result) {
if ($result['http_code'] == 200) {
$status = "Worked!";
} else {
$status = "FAILED with {$result['http_code']}";
}
if ($result["redirect_count"] > 0) {
array_push($redirected_urls, $url);
echo "{$url}: ${status}\n";
} else {
echo "{$url}: redirected to {$result['url']} and {$status}\n";
}
}
}
//
// Handle redirected URLs
//
echo "Processing redirected URLs...\n";
for ($i = 0 ; $i < count($redirected_urls) ; $i += $request_limit) {
$results = fetch_urls(array_slice($redirected_urls, $i, $request_limit), array(CURLOPT_FOLLOWLOCATION => false));
foreach($results as $url => $result) {
if ($result['http_code'] == 301) {
echo "{$url} permanently redirected to {$result['url']}\n";
} else if ($result['http_code'] == 302) {
echo "{$url} termporarily redirected to {$result['url']}\n";
} else {
echo "{$url}: FAILED with {$result['http_code']}\n";
}
}
}
The above code processes a list of URLs in batches. It works in two passes. In the first pass, each request is configured to follow redirects and simply reports whether each URL ultimately lead to a successful request, or a failure.
The second pass processes any redirected URLs detected in the first pass and reports whether the redirect was a permanent redirection (meaning you can update your database with the new URL), or temporary (meaning you should NOT update your database).
NOTE:
In your original code, you have the following line, which will not work the way you expect it to:
else if ($urlinfo['http_code'] == 301 || 302)
The expression will ALWAYS return TRUE. The correct expression is:
else if ($urlinfo['http_code'] == 301 || $urlinfo['http_code'] == 302)
Also, put
set_time_limit(0);
at the top of your script to stop it aborting when it hits 30 seconds.
When using the Facebook Graph API to return more than 500 elements (like a friend list) paging is required. What's a good way to do this?
Here is the way that I use paging on my own apps.
http://developsocialapps.com/facebook-friends-list-and-paging/
The library has most of the code needed. The main method is getGraphObjectWithPaging. It gets the object with the graph API and then keeps looping as long as there is a next page in the response or the $maxpages has been reached. One peculiarity is that sometimes Facebook returns the next page as the same page you just got, so it checks for this and stops at that point too.
class FacebookApp {
public $appId;
private $appSecret;
private $nameSpace;
public $userId;
public $token;
public $tokenExpires;
// get your own from http://www.w3.org/P3P/
public $p3p = 'P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"';
/* construct object
appid, secret, and namespace from app settings */
public function __construct($id, $secret, $namespace) {
$this->appId = $id;
$this->appSecret = $secret;
$this->nameSpace = $namespace;
}
/* return json data from a graph api object using paging
$object = object to get
limit = limit parameter for API object
maxpages = maximum number of pages to get */
function getGraphObjectWithPaging($object,$limit=500,$maxpages=10) {
$data = array();
$url = $this->getGraphUrl($object,$limit);
// loop through API calls until maxpages or no paging->next
while ($maxpages > 0) {
$response = $this->makeCurlRequest($url);
if ($repsonse === false) {
// something went wrong
break;
} else {
$jsonarray = json_decode($response,true);
if (isset($jsonarray['error'])) {
// something went wrong
break;
} else {
// add current data to data array
$data = array_merge ($data,$jsonarray['data']);
if (isset($jsonarray['paging']['next'])) {
if ($url == $jsonarray['paging']['next']) {
// for some reason facebook sometimes returns a next url which is the same as we just got, so exit here
break;
} else {
// keep looping
$url = $jsonarray['paging']['next'];
$maxpages--;
}
} else {
// no more pages
break;
}
}
}
}
return array("data"=>$data); // using data so it is the same format as other API repsonses
}
/* constructs graphs url */
public function getGraphUrl($object,$limit=false) {
$url = "https://graph.facebook.com/".$object;
if (strpos($url,"?") === false) $url .= "?";
else $url .= "&";
$url .= "access_token=".$this->token;
if ($limit !== false) $url .= "&limit=".$limit;
return $url;
}
/* uses curl to get a url, use $postarray to make a post, otherwise it will get */
public function makeCurlRequest($url,$postarray=false) {
$return = false;
try {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if($postarray !== false){
curl_setopt ($ch, CURLOPT_POST, true);
curl_setopt ($ch, CURLOPT_POSTFIELDS, $postarray);
}
$response = curl_exec($ch);
$responseInfo = curl_getinfo($ch);
curl_close($ch);
if ($responseInfo['http_code']==200) {
$return = $response;
}
} catch (Exception $e) {
$return = false;
}
return $return;
}
/* sets userid and token from signed request, return true or false if authorized */
public function initOauthUserFromSignedRequest() {
$authorized = false;
if (isset($_REQUEST['signed_request'])) {
$data = $this->parseSignedRequest($_REQUEST['signed_request']);
if ($data !== false) {
if (isset($data['user_id']) && isset($data['oauth_token'])) {
$this->userId = $data['user_id'];
$this->token = $data['oauth_token'];
$this->tokenExpires = $data['expires'];
$authorized = true;
}
}
}
return $authorized;
}
/* require user to authorize and have permissions for page
redirect_uri = url to return after user has authorized like redirect.php
success_uri = url to redirect to on successful authorization like mypage.php
scope = comma separted list of permissions */
function requireAuthorization($redirect_uri,$success_uri=false,$scope=false) {
if ($success_uri === false) {
// if no success_uri use current page, all files for app must be in same directory
$success_uri = substr($_SERVER['REQUEST_URI'],strrpos($_SERVER['REQUEST_URI'],"/")+1);
}
$this->setCookie ("success_uri",$success_uri,0); // we will use this on the redirect_uri page
$requireauth = true;
if ($this->initOauthUserFromSignedRequest()) { // user has authorized
if (($scope === false) || ($this->hasAllPermissions($scope))) { // now check for perms
$requireauth = false;
}
}
if ($requireauth) { // user is either not authorized or doesn't have permissions
$url = $this->getAuthUrl($this->getCanvasUrl($redirect_uri),$scope);
echo "<html>\n<body>\n<script>\ntop.location.href='".$url."';\n</script></body></html>";
exit();
}
}
/* checks to see if has permissions, scope is comma separated list */
public function hasAllPermissions($scope) {
$return = false;
$cookiename = "permissions_".$this->appId."_".$this->userId;
$requiredpermissions = explode(",",$scope);
// first check cookie
if (isset($_COOKIE[$cookiename])) {
$return = true;
$permissions = json_decode($_COOKIE[$cookiename],true);
foreach ($requiredpermissions as $perm) {
if ($permissions['data'][0][$perm] != 1) {
$return = false;
break;
}
}
}
// if didn't have all in cookie, then see if it is in graph
if ($return == false) {
$permissions = $this->getGraphObject("me/permissions");
if ($permissions !== false) {
$this->setCookie($cookiename,json_encode($permissions),0);
$return = true;
foreach ($requiredpermissions as $perm) {
if ($permissions['data'][0][$perm] != 1) {
$return = false;
break;
}
}
}
}
return $return;
}
/* sets a cookie with p3p headers */
public function setCookie($name,$value,$expires) {
if ($this->p3p != '') {
header($this->p3p);
$this->p3p = '';
}
setcookie ($name,$value,$expires,"/");
}
/* returns url for oauth authorization
redirect_uri = url to return after user has authorized
scope = comma separted list of permissions */
public function getAuthUrl($redirect_uri,$scope=false) {
$url = "https://www.facebook.com/dialog/oauth/?client_id=".$this->appId."&redirect_uri=".rawurlencode($redirect_uri);
if ($scope !== false) $url .= "&scope=".rawurlencode($scope);
return $url;
}
/* returns url to app canvas page, $page like mypage.php?foo=bar */
public function getCanvasUrl($page) {
if ($_SERVER['HTTPS'] == "on") $protocol = "https";
else $protocol = "http";
return $protocol."://apps.facebook.com/".$this->nameSpace."/".$page;
}
/* parses signed_request parameter and returns data object, returns false if sigs don't match */
public function parseSignedRequest($signed_request) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
$data = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);
$sig = base64_decode(strtr($encoded_sig, '-_', '+/'));
$expected_sig = hash_hmac('sha256', $payload, $this->appSecret, true);
if ($sig == $expected_sig) {
return $data;
} else {
return false;
}
}
}
Here is how to use it on a page:
$facebookapp = new FacebookApp($GLOBALS['facebookAppId'],$GLOBALS['facebookAppSecret'],$GLOBALS['facebookNamespace']);
$facebookapp->requireAuthorization($GLOBALS['facebookRedirectPage']);
$friends = $facebookapp->getGraphObjectWithPaging("me/friends");
I have a website that first uses the Facebook Javascript API to log a user in. However, when a user sends an xmlhttp request to one of my PHP scripts I would like to check again to see if that user is logged in via PHP (for securities sake).
I had a working system but after a site makeover there seems to be a bug. When I echo $html in the facebook.php script I get this error: {"error":{"message":"An active access token must be used to query information about the current user.","type":"OAuthException"}}.
/* request.js */
var postData = "id=" + id;
sendRequest('assets/php/believe.php',function(req) {
console.log(req.responseText);
},postData);
/* believe.php */
<?php
include("facebook.php");
$id = intval($_POST["id"]);
if($id == '') {
//Stuff
} else {
if($cookie) {
echo "hey";
}
}
?>
/* facebook.php */
<?php
define('YOUR_APP_ID', 'xxxxxx');
define('YOUR_APP_SECRET', 'xxxxxxxxxxxx');
function curl_get_contents($url){
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_URL, $url);
$html = curl_exec($curl);
curl_close($curl);
echo "$html";
return $html;
}
function get_facebook_cookie($app_id, $app_secret){
$args = array();
parse_str(trim($_COOKIE['fbs_' . $app_id], '\\"'), $args);
ksort($args);
$payload = '';
foreach ($args as $key => $value){
if ($key != 'sig'){
$payload .= $key . '=' . $value;
}
}
if (md5($payload . $app_secret) != $args['sig']){
return null;
}
return $args;
}
$cookie = get_facebook_cookie(YOUR_APP_ID, YOUR_APP_SECRET);
$user = json_decode(curl_get_contents(
'https://graph.facebook.com/me?access_token=' .
$cookie['access_token']));
?>
I think your code is fine. User access token is expired. If user renews it, the code will work. Make sure you have a valid token in the cookie