send a pageview event via Measurement Protocol to a GA4 property - php

How can I send a pageview event via Measurement Protocol to a GA4 property with PHP?
This is how I'm doing, but inside my Google Analytics 4 property I can't see any traffic.
$data = array(
'api_secret' => 'XXXX-YYYYY',
'measurement_id' => 'G-12345678',
'client_id' => gen_uuid(), // generates a random id
'events' => array(
'name' => 'page_view',
'params' => array(),
)
);
$url = 'https://www.google-analytics.com/mp/collect';
$content = http_build_query($data);
$content = utf8_encode($content);
$ch = curl_init();
curl_setopt($ch,CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_HTTPHEADER,array('Content-type: application/x-www-form-urlencoded'));
curl_setopt($ch,CURLOPT_HTTP_VERSION,CURL_HTTP_VERSION_1_1);
curl_setopt($ch,CURLOPT_POST, TRUE);
curl_setopt($ch,CURLOPT_POSTFIELDS, $content);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);

I'm working on registering pageviews to track API usage right now, here's what I've found:
XTOTHEL is right about setting the content type to content/json above. In addition to specifying the content type you also have to send JSON data as the CURLOPT_POSTFIELDS data.
Also per their specification the api_secret and measurement_id need to be part of the URI: https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#required_parameters
Lastly, you can use debug mode to validate your responses and figure out what's going on now by simply changing the URL to google-analytics.com/debug/mp/collect
Here's the code I'm working with right now:
//retrieve or generate GA tracking id
if (empty($_COOKIE['_cid'])) {
setcookie('_cid', vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4)));
}
$data = '{"client_id":"'.$_COOKIE['_cid'].'","events":[{"name":"load_endpoint","params":{"page_location":"'.$request->fullUrl().'"}}]}';
echo '<pre>';
print_r($data);
$measurement_id = 'G-xxxxx';
$api_secret = 'xxxx';
$url = 'https://www.google-analytics.com/debug/mp/collect?api_secret='.$api_secret.'&measurement_id='.$measurement_id;
$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
This works to a certain extent. Currently it's registering the page view as a custom event instead of an actual pageview though. I'm still trying to figure out how to get them to come through as page views.
Follow up
After a little more debugging I figured out page views are actually working, they just weren't showing up in some of the views. The fix for that was to add page_title into the params:
$data = '
{
"client_id": "'.$_COOKIE['_cid'].'",
"events": [
{
"name": "page_view",
"params": {
"page_location": "'.$request->fullUrl().'",
"page_title": "'.$request->path().'"
}
}
]
}
';
A few extra notes for whoever comes next:
Debug mode did return some useful validation errors for invalid top-level parameters (client_id, events) - but it didn't return errors for anything inside of the "params" for events. IE - i put "page_asdtitle" instead of "page_title" and it accepted it just fine.
None of the tests I sent through actually showed up in the debug panel while using debug mode. I suspect this is because of the data propagation delay, it's probably not loading realtime data.
Using a JSON validator can help. Make sure you use objects and arrays where GA tells you to.
If you get stuck figuring out why your PHP code doesn't work, write the code as a browser event in JavaScript and run it in your browser. There's tons of examples on how to do that. From there, you can use Dev Tools -> Network to inspect the request. If you right click on the google analytics request to the 'collect' endpoint you'll see an option to Copy Request as CURL. Put that into a text editor and compare it to what your PHP code is sending.
To ACTUALLY test this without the massive propagation delay you can login to Google Analytics, go to Reports -> Realtime, and you should see your data show up within 30-60 seconds if it's working. Realtime data will NOT show up if you're using the /debug/ endpoint though.

Related

Run PHP + Curl in background after user abort

I've got an online store and I make use of an online accounting software where I manually post orders to. The online accounting software has a very big api and I would like to send orders over automatically when a customer places an order.
Once an order is completed the customer lands up on the Success Page, i.e. successpage.php
In this page I've got the following:
$sendOrder = file_get_contents("https://myonlinestore.com/sendorder.php?order=1234");
On sendorder.php, I receive the $_GET parameter "order" which is the order number, and the I process several SQL requests to retrieve data of the order from the database.
Once I've got all this data, I then initiate a CURL post to send the data using the API of the accounting system.
Here is a watered-down version of my code that contains the essential parts:
$orderNum = htmlspecialchars($_GET["order"]) // SENT OVER FILE_GET_CONTENTS
// bOf process SQL here and get order info stored in various variables
// EXECUTE SQL HERE
// eOf process SQL here and get order info stored in various variables
$invoice = array(
'customer_id' => $custaccount,
'estimate_number' => $orderRef,
'reference_number' => $orderNum
// MANY OTHER VARIABLES ENTERED HERE, BUT LEFT OUT TO KEEP THINGS SHORT
);
$jsonInvoice = json_encode($invoice);
$url = 'https://ACCOUTINGAPP.com/api/v2/orders';
$data = array(
'authtoken' => '***********',
'JSONString' => $jsonInvoice,
'company_id' => '***********'
);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/x-www-form-urlencoded") );
$response = false;
$response = curl_exec($ch);
curl_close($ch);
// TEST RESPONSE
if($response !== false) {
var_dump($response);
}
else
{
echo "oops error hehehe";
}
MY MAIN CONCERN:
I expect the user to immediately close the tab or page once they're on successpage.php.
But I would like to ensure that the successpage.php's $sendOrder = file_get_contents() and the code that it executes on sendorder.php continues running regardless of user connection.
So my question is, where would I put:
ignore_user_abort(TRUE);
Also, should I use output buffering? I'm only asking because I read a post about this on some other website and it advised this.
And lastly, should I include:
set_time_limit(0);
Call ignore_user_abort(TRUE); as soon as you can. And you do not need output buffering as noone is going to see your output once browser tab is closed anywyay, so you just need to ensure your script continues if it was already doing anything.

Send Push notification with Flutter + OneSignal for PlayerID

I'm creating a push, calendar-style push system. I need when I create a schedule for the user the system send a notification only to him, I created the system to manage this PHP, does anyone know how to help me?
If you're using OneSignal. You can send to that individual device with Playerid, you do however have to store the playerid serverside so that you know which device to send to. I personally do that in init state and do a http.post to my api to save the playerid into my database for that particular user.
You can of course achieve the same by using OneSignal's tags (useful if same person has multiple accounts in one device).
To send notification, use curl in php.
<?php
function sendMessage(){
$content = array(
"en" => 'English Message'
);
$fields = array(
'app_id' => "your-app-id",
'include_player_ids' => array("playerid-you-want-to-send-to"),
'data' => array("foo" => "bar"),
'contents' => $content
);
$fields = json_encode($fields);
print("\nJSON sent:\n");
print($fields);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://onesignal.com/api/v1/notifications");
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json; charset=utf-8'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
$response = sendMessage();
$return["allresponses"] = $response;
$return = json_encode( $return);
print("\n\nJSON received:\n");
print($return);
print("\n");
?>
In Flutter, get the package, import it and:
void oneSignal() {
OneSignal.shared.init("app-id");
OneSignal.shared.setNotificationReceivedHandler((OSNotification notification)
{
//do what you need to do with upcoming notification, get title for example
print(notification.payload.title);
}
}
I'm far from an expert on mobile apps, so somebody should correct / confirm this.
To accomplish push notification in your app you could use a 'live' connection (websocket for instance) or you could use polling.
I don't know much about websockets and I don't think that's possible with CakePHP (not sure). EDIT: Definitely not possible out-of-the-box, but plugins exist.
When you are using polling you repeat a GET request every so often (once per hour, once per minute, depending on needs) and check if there is new info.
For instance, your CakePHP page could be an action taking a lastUpdated argument, which returns new information since that timestamp. The app then requests this page every x minutes, each time setting the lastUpdated parameter. When there is new info, the response will be not empty and the app can process it.
This does mean the app needs to always run in the background and the number of requests can become sizeable (depending on the polling interval).

php+curl to send a post request with fields

trying to send post request to api, to get an image back.
example url:
https://providers.cloudsoftphone.com/lib/prettyqr/createQR.php?user=1003123&format=png&cloudid=asdasdasd&pass=123123123
the above url works fine in the browser,
the api doesnt care if the request is get/post,
result of my code is always 'invalid input'.
code:
$url='https://providers.cloudsoftphone.com/lib/prettyqr/createQR.php';
$u = rand();
$p = rand();
$fields = array(
'user'=> urlencode($u),
'pass'=> urlencode($p),
'format'=> urlencode('jpg'),
'cloudid' => urlencode('test')
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
on a side note: is there a way to debug the request in order to see what is being sent ?
The URL provided isn't working for POST request. Here is resulting screenshot (I tried using Advance Rest Client)
However Its working perfectly with GET method. So you can continue using GET request method to generate QR code.
I agree that GET isn't much secure compare to POST method but in your case while requesting from curl user won't get to know about such URL parameters (userid, password). Because curl request will be sending from your web server and not from client/user's browser.
Later you can just output the response image you got from the api.

How to create category in Disqus

I need to creating some categories in Disqus. I tried to do it by Javascript but it cannot do because require POST request but JSONP only work with GET request. After that, I tried to use CURL in server-side, there are my code
public function createDisqusCategory($title, $forum)
{
$access_token = ACCESS_TOKEN;
$secret_key = SECRET_KEY;
$public_key = PUBLIC_KEY;
$url = 'https://disqus.com/api/3.0/categories/create.json';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER,array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "access_token=$access_token&api_secret=$secret_key&api_key=$public_key&forum=$forum&title=$title");
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
and it response {"code": 22, "response": "You do not have admin privileges on forum '...'"}
How can I solve this problem?
Does your application have Default access set to "Read, write and manage forums"? If not, you'll either need to add a "scope" parameter to your POSTFIELDS, or set default access to manage forums in your application settings. Here's our documentation on scopes: http://disqus.com/api/docs/permissions/
On another note, categories in Disqus are limited to use with the API, so it's not useful in any way unless you're querying comments/threads using a custom script. If you are, I'd also advise keeping it to about 5 categories maximum, or else it can really slow down queries.

Scraping a Webpage for Results Using PHP cURL - Post Not Working

I'm new to using cURL, but from what I have read, the following should post the variables to the page, then print the result. The result prints, but it doesn't seem like the POST variables went because no results are generated. FireBug doesn't show anything going either. Any ideas what I'm doing wrong?
Thanks for your help!
// create curl resource
$ch = curl_init();
// set url
curl_setopt($ch, CURLOPT_URL, "http://butlercountyclerk.org/bcc-11112005/ForeclosureSearch.aspx");
$data = array(
'Search:btnSearch' => 'Search',
'Search:ddlMonth' => '1',
'Search:ddlYear' => '2011'
);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// $output contains the output string
$output = curl_exec($ch);
// close curl resource to free up system resources
curl_close($ch);
echo $output;
Based on the coding of the site, it appears that you're missing a number of variables. Take for example, the actual post request made to the search page:
__VIEWSTATE=dDwtMjk2Mjk5NzczO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDE+Oz47bDx0PDtsPGk8Mz47aTwxOT47PjtsPHQ8dDw7cDxsPGk8MD47aTwxPjtpPDI+O2k8Mz47aTw0PjtpPDU+Oz47bDxwPDIwMDY7MjAwNj47cDwyMDA3OzIwMDc+O3A8MjAwODsyMDA4PjtwPDIwMDk7MjAwOT47cDwyMDEwOzIwMTA+O3A8MjAxMTsyMDExPjs+Pjs+Ozs+O3Q8QDA8Ozs7Ozs7Ozs7Oz47Oz47Pj47Pj47Pj47PmVlaXw5JK161vti9TC+QMdeTNQI&Search:ddlMonth=1&Search:ddlYear=2011&Search:txtCompanyName=&Search:txtLastName=&Search:txtCaseNumber=&Search:btnSearch=Search
This is post-feeding though URLDecode by the way. What this means though, is that your array of 3 values is missing data. At the very least, I'd suspect that Search:btnSearch=Search is missing, and would suggest that you implement all fields into your POST request.

Categories