So, I have an image link that has this href:
http://www.app.com/link?target=www.target.com¶m1=abc¶m2=xyz
This is processed like so (I use laravel):
function out (Request $request) {
$url = $request->target;
$qs = $request->except('target');
if ( !empty($qs) ) {
$url .= strpos($url, '?') !== false ? '&' : '?';
$url .= http_build_query($qs);
}
return redirect($url);
}
Most of the time, this works. However, lately, we've been experiencing an issue where param1 and param2 are attached to the URL in a seemingly infinite loop causing us to hit a 414 Request URI too long Error.
The problem is that it happens so randomly that I really don't know where to check because I added a checker before the return statement.
if ( substr_count($url, 'param1') > 1 ) {
$file = storage_path() . '/logs/logger.log';
$log = "[ " . date("d-m-Y H:i:sa") . " ] [ {$request->ip()} ] - {$url} \n";
file_put_contents($file, $log, FILE_APPEND);
}
And it hasn't logged a single hit. Even after our testers experienced the bug.
Is it possible that the receiving application is breaking the URL somehow?
What information should I be looking out for? Have you seen an issue like this before?
Is it the http_build_query that could be causing this and that my checker just doesn't work as expected (though, I did test it and it logged my test URL).
Any help on the matter would be great.
Assuming and issue with http_build_query:
Well, one attempt you may try is to rewrite the code without $request->except and http_build_query.
If you don't have any special reason to use http_build_query i would suggest to use $request->input.
Example with $request->input:
function out (Request $request) {
$url = $request->target;
$param1 = $request->input('param1', '');
$param2 = $request->input('param2', '');
if (!empty($param1) || !empty($param2)) {
$url .= '?';
}
if (!empty($param1) && !empty($param2)) {
$url .= 'param1=' . $param1 . '¶m2=' . $param2;
} else {
$url .= !empty($param1) 'param1=' . $param1 : '';
$url .= !empty($param2) 'param2=' . $param2 : '';
}
return redirect($url);
}
The solution is a little bit more verbose but with that, you should be sure 100% that is not the code to generate the redundancy.
Absurd, remote possibility:
The second thing I would try is to check you log system. For instance if you are running under apache you should have a file called access.log under /var/log/apache2/ (or under /var/log/nginx/ with nginx).
In there you should have the history of all your http requests.
Maybe there is a chance that some of the wired requests with multiple params are from a strange IP address.
If this is the case, it means that some company is monitoring and testing the website (potentially with the strange parameters) for security reasons.
If this is the case, I guess you are under http and you should switch to https.
Anyway, with the new code, you should be sure about the code and be able to investigate any other part of the system.
Related
I'm calling through Axios a PHP script checking whether a URL passed to it as a parameter can be embedded in an iframe. That PHP script starts with opening the URL with $_GET[].
Strangely, a page with cross-origin-opener-policy: same-origin (like https://twitter.com/) can be opened with $_GET[], whereas a page with Referrer Policy: strict-origin-when-cross-origin (like https://calia.order.liven.com.au/) cannot.
I don't understand why, and it's annoying because for the pages that cannot be opened with $_GET[] I'm unable to perform my checks on them - the script just fails (meaning I get no response and the Axios call runs the catch() block).
So basically there are 3 types of pages: (1) those who allow iframe embeddability, (2) those who don't, and (3) the annoying ones who not only don't but also can't even be opened to perform this check.
Is there a way to open any page with PHP, and if not, what can I do to prevent my script from failing after several seconds?
PHP script:
$source = $_GET['url'];
$response = true;
try {
$headers = get_headers($source, 1);
$headers = array_change_key_case($headers, CASE_LOWER);
if (isset($headers['content-security-policy'])) {
$response = false;
}
else if (isset($headers['x-frame-options']) &&
$headers['x-frame-options'] == 'DENY' ||
$headers['x-frame-options'] == 'SAMEORIGIN'
) {
$response = false;
}
} catch (Exception $ex) {
$response = $ex;
}
echo $response;
EDIT: below is the console error.
Access to XMLHttpRequest at 'https://path.to.cdn/iframeHeaderChecker?url=https://calia.order.liven.com.au/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
CustomLink.vue?b495:61 Error: Network Error
at createError (createError.js?2d83:16)
at XMLHttpRequest.handleError (xhr.js?b50d:84)
VM4758:1 GET https://path.to.cdn/iframeHeaderChecker?url=https://calia.order.com.au/ net::ERR_FAILED
The error you have shown is coming from Javascript, not from PHP. get_headers() returns false on failure, it will not throw an exception - the catch() never happens. get_headers() just makes an http request, like your browser, or curl, and the only reason that would fail is if the URL is malformed, or the remote site is down, etc.
It is the access from http://localhost:3000 to https://path.to.cdn/iframeHeaderChecker with Javascript that has been blocked, not PHP access to the URLs you are passing as parameters in $_GET['url'].
What you're seeing is a standard CORS error when you try to access a different domain than the one the Javascript is running on. CORS means Javascript running on one host cannot make http requests to another host, unless that other host explicitly allows it. In this case, the Javascript running at http://localhost:3000 is making an http request to a remote site https://path.to.cdn/. That's a cross-origin request (localhost !== path.to.cdn), and the server/script receiving that request on path.to.cdn is not returning any specific CORS headers allowing that request, so the request is blocked.
Note though that if the request is classed as "simple", it will actually run. So your PHP is working already, always, but bcs the right headers aren't returned, the result is blocked from being displayed in your browser. This can lead to confusion bcs for eg you might notice a delay while it gets the headers from a slow site, whereas it is super fast for a fast site. Or maybe you have logging which you see is working all the time, despite nothing showing up in your browser.
My understanding is that https://path.to.cdn/iframeHeaderChecker is your PHP script, some of the code of which you have shown in your question? If so, you have 2 choices:
Update iframeHeaderChecker to return the appropriate CORS headers, so that your cross-origin JS request is allowed. As a quick, insecure hack to allow access from anyone and anywhere (not a good idea for the long term!) you could add:
header("Access-Control-Allow-Origin: *");
But it would be better to update that to more specifically restrict access to only your app, and not everyone else. You'll have to evaluate the best way to do that depending on the specifics of your application and infrastructure. There many questions here on SO about CORS/PHP/AJAX to check for reference. You could also configure this at the web server level, rather than the application level, eg here's how to configure Apache to return those headers.
If iframeHeaderChecker is part of the same application as the Javascript calling it, is it also available locally, on http://localhost:3000? If so, update your JS to use the local version, not the remote one on path.to.cdn, and you avoid the whole problem!
This is just my rough guess about what wrong with your code can be.
I noticed you do:
a comparison of values from $headers but without
ensuring they have the same CAPITAL CASE as the values you compare against. Applied: strtoupper().
check with isset() but not test if key_exist before
Applied: key_exist()
check with isset() but perhaps you should use !empty() instead of isset()
compare result:
$value = "";
var_dump(isset($value)); // (bool) true
var_dump(!empty($value)); // (bool) false
$value = "something";
var_dump(isset($value)); // (bool) true
var_dump(!empty($value)); // (bool) true
unset($value);
var_dump(isset($value)); // (bool) false
var_dump(!empty($value)); // (bool) false
The code with applied changes:
<?php
error_reporting(E_ALL);
declare(strict_types=1);
header('Access-Control-Allow-Origin: *');
ob_start();
try {
$response = true;
if (!key_exists('url', $_GET)) {
$msg = '$_GET does not have a key "url"';
throw new \RuntimeException($msg);
}
$source = $_GET['url'];
if ($source !== filter_var($source, \FILTER_SANITIZE_URL)) {
$msg = 'Passed url is invaid, url: ' . $source;
throw new \RuntimeException($msg);
}
if (filter_var($source, \FILTER_VALIDATE_URL) === FALSE) {
$msg = 'Passed url is invaid, url: ' . $source;
throw new \RuntimeException($msg);
}
$headers = get_headers($source, 1);
if (!is_array($headers)) {
$msg = 'Headers should be array but it is: ' . gettype($headers);
throw new \RuntimeException($msg);
}
$headers = array_change_key_case($headers, \CASE_LOWER);
if ( key_exists('content-security-policy', $headers) &&
isset($headers['content-security-policy'])
) {
$response = false;
}
elseif ( key_exists('x-frame-options', $headers) &&
(
strtoupper($headers['x-frame-options']) == 'DENY' ||
strtoupper($headers['x-frame-options']) == 'SAMEORIGIN'
)
) {
$response = false;
}
} catch (Exception $ex) {
$response = "Error: " . $ex->getMessage() . ' at: ' . $ex->getFile() . ':' . $ex->getLine();
}
$phpOutput = ob_get_clean();
if (!empty($phpOutput)) {
$response .= \PHP_EOL . 'PHP Output: ' . $phpOutput;
}
echo $response;
Using Throwable instead of Exception will also catch Errors in PHP7.
Keep in mind that:
$response = true;
echo $response; // prints "1"
but
$response = false;
echo $response; // prints ""
so for the $response = false you'll get an empty string, not 0
if you want to have 0 for false and 1 for true then change the $response = true; to $response = 1; for true and $response = false; to $response = 0; for false everywhere.
I hope that somehow helps
SOLVED!! Sorry for wasting your time.
Problems:
Second function "verify_webhook_2" always returns false.
Code in if statement runs whether tests return true or not.
I copied and pasted the first function, then made (what I would think to be) appropriate changes, so I can verify webhooks coming from two different Shopify stores. I'm sure it's something simple that I am just oblivious to, as I'm still fairly new to all of this. If I change $verify to the secret for $verify2 then webhooks received from that shop will verify true.
And I cannot for the life of me understand why the code in the if statement runs even when both requirements test false. There's no way I can think of that either could prove true when the receiving a webhook from the shop related to the $verify2 secret. Probably a rookie mistake?
$verify = "xxxxsecretxxxx";
$verify2 = "xxxxsecretxxxx";
define('SHOPIFY_APP_SECRET', $verify);
define('SHOPIFY_APP_SECRET_2', $verify2);
function verify_webhook($data, $hmac_header)
{
$calculated_hmac = base64_encode(hash_hmac('sha256', $data, SHOPIFY_APP_SECRET, true));
return hash_equals($hmac_header, $calculated_hmac);
}
function verify_webhook_2($data, $hmac_header)
{
$calculated_hmac_2 = base64_encode(hash_hmac('sha256', $data, SHOPIFY_APP_SECRET_2, true));
return hash_equals($hmac_header, $calculated_hmac_2);
}
$hmac_header = $_SERVER['HTTP_X_SHOPIFY_HMAC_SHA256'];
$data = file_get_contents('php://input');
$verified = verify_webhook($data, $hmac_header);
$verified_2 = verify_webhook_2($data, $hmac_header);
error_log('Webhook verified: '.var_export($verified, true)); //check error.log to see the result
if ($verified == true || $verified_2 == true){
header("HTTP/1.1 200 OK"); //respond with success
http_response_code(201); //respond with success
file_put_contents('/var/www/html/temp/webhook.json', $data);
$POST = json_decode(file_get_contents('/var/www/html/temp/webhook.json'), true);
//$POST = $POST['id'];
$report = "id: " . $POST['id'] . " - email: " . $POST['email'] . " - name: " . $POST['customer']['first_name'] . " " . $POST['customer']['last_name'] ;
}else{
}
Of course, right after posting the question, I realized my failure. I had only written to the error log for the first function's comparison, so when I kept seeing "webhook verified: false" in the error logs, I assumed that was regardless of the shop I was sending data from.
I added:
error_log('Webhook verified_2: '.var_export($verified_2, true)); //check error.log to see the result
just below the first error_log call, then added another error log into the else section of my if statement, and all is working correctly, and responding correctly.
It was a lack of understanding on my part that led to me believing it was not working correctly, when in fact, everything was, but I was missing information.
My client contacts with the database via service. When I do some action, I call the service URL with file_get_contents then process the response.
I think when a user enters the site from google search result, I think google adds some parameters to my service URL which belongs to file_get_contents.
For example,
as it should be,
file_get_contents(service.domain/service_name?param1=0)
but google adds some strange parameters at the end of url. like this:
file_get_contents(service.domain/service_name?param1=0&force=1)
I saw two or three strange parameters so,
force = 1
gclid=CNC-jvTapbcCFaaj
I don't know how can I handle and remove these parameters.
How can I remove these strange parameters before calling the URL with file_get_contents?
This is my function:
public function getWhere($keyword)
{
$locale = session('locale');
if($locale != "en" && $locale != "")
{
$locale = "_".session('locale');
}else{
$locale = "";
}
$page = file_get_contents(getenv('API_URL')."station_info?search.url=".$keyword);
$page = json_decode($page);
$page = (array) $page->rows;
$station = $page[0];
if($station->search->status == 1)
{
return redirect('/');
}
return view('station', compact('station', 'locale'));
}
Strange parameters add themselves end of the URL.
Edit
I think someone is trying to something. $keyword must take a string which is station name, example, London.
But someone is trying to send London&force=1 so I have to check the $keyword variable.
You can parse url by parse_url function, remove unwanted parameters or just keep parameters you want and build url back
after using parse_url function you may need to parse query by parse_str function, so code will look like:
$urlParsed = parse_url("service.domain/service_name?param1=0&force=1");
$params = parse_str ($urlParsed['query']);
$newParams = [];
$neededParams = ['param1', 'param2'];
foreach ($neededParams as $p) {
if (isset($params[$p])) {
$newParams[$p] = $params[$p];
}
}
$newUrl = $urlParsed['scheme'] . '://' . $urlParsed['host'] . $urlParsed['path'] . '?' . http_build_query($newParams);
file_get_contents($newUrl);
I didn't test that code, but I hope the idea is clear and you can amend it to your needing.
I need to detect if a provided URL matches the one currently navigated to. Mind you the following are all valid, yet semantically equivalent URLs:
https://www.example.com/path/to/page/index.php?parameter=value
https://www.example.com/path/to/page/index.php
https://www.example.com/path/to/page/
https://www.example.com/path/to/page
http://www.example.com/path/to/page
//www.example.com/path/to/page
//www/path/to/page
../../../path/to/page
../../to/page
../page
./
The final function must return true if the given URL points back to the current page, or false if it does not. I do not have a list of expected URLs; this will be used for a client who just wants links to be disabled when they link to the current page. Note that I wish to ignore parameters, as these do not indicate the current page on this site. I got as far as using the following regex:
/^((https?:)?\/\/www(\.example\.com)\/path\/to\/page\/?(index.php)?(\?.+=.*(\&.+=.*)*)?)|(\.\/)$/i
where https?, www, \.example\.com, \/path\/to\/page, and index.php are dynamically detected with $_SERVER["PHP_SELF"] and made into regex form, but that doesn't match the relative URLs like ../../to/page.
EDIT: I got a bit farther with the regex: refiddle.com/gv8
now I'd just need PHP to dynamically create the regex for any given page.
First off, there is no way to predict the total list of valid URLs that will result in display of the current page, since you can't predict (or control) external links that might link back to the page. What if someone uses TinyURL or bit.ly? A regex will not cut the mustard.
If what you need is to insure that a link does not result in the same page, then you need to TEST it. Here's a basic concept:
Every page has a unique ID. Call it a serial number. It should be persistent. The serial number should be embedded somewhere predictable (though perhaps invisibly) within the page.
As the page is created, your PHP will need to walk through all the links for each page, visit each one, and determine whether the link resolves to a page with a serial number that matches the calling page's serial number.
If the serial number does not match, display the link as a link. Otherwise, display something else.
Obviously, this will be an arduous, resource-intensive process for page production. You really don't want to solve your problem this way.
With your "ultimate goal" comment in mind, I suspect your best approach is to be approximate. Here are some strategies...
First option is also the simplest. If you're building a content management system that USUALLY creates links in one format, just support that format. Wikipedia's approach works because a [[link]] is something THEY generate, so THEY know how it's formatted.
Second is more the direction you've gone with your question. The elements of a URL are "protocol", "host", "path" and "query string". You can break them out into a regex, and possibly get it right. You've already stated that you intend to ignore the query string. So ... start with '((https?:)?//(www\.)?example\.com)?' . $_SERVER['SCRIPT_NAME'] and add endings to suit. Other answers are already helping you with this.
Third option is quite a bit more complex, but gives you more fine-grained control over your test. As with the last option, you have the various URL elements. You can test for the validity of each without using a regex. For example:
$a = array(); // init array for valid URLs
// Step through each variation of our path...
foreach([$_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']] as $path) {
// Step through each variation of our host...
foreach ([$_SERVER['HTTP_HOST'], explode(".", $_SERVER['HTTP_HOST'])[0]] as $server) {
// Step through each variation of our protocol...
foreach (['https://','http://','//'] as $protocol) {
// Set the URL as a key.
$a[ $protocol . $server . $path ] = 1;
}
}
// Also for each path, step through directories and parents...
$apath=explode('/', $path); // turn the path into an array
unset($apath[0]); // strip the leading slash
for( $i = 1; $i <= count($apath); $i++ ) {
if (strlen($apath[$i])) {
$a[ str_repeat("../", 1+count($apath)-$i) . implode("/", $apath) ] = 1;
// add relative paths
}
unset($apath[$i]);
}
$a[ "./" . implode("/", $apath) ] = 1; // add current directory
}
Then simply test whether the link (minus its query string) is an index within the array. Or adjust to suit; I'm sure you get the idea.
I like this third solution the best.
A regex isn't actually necessary to strip off all the query parameters. You could use strok():
$url = strtok($url, '?');
And, to check the output for your URL array:
$url_list = <<<URL
https://www.example.com/path/to/page/index.php?parameter=value
https://www.example.com/path/to/page/index.php
...
./?parameter=value
./
URL;
$urls = explode("\n", $url_list);
foreach ($urls as $url) {
$url = strtok($url, '?'); // remove everything after ?
echo $url."\n";
}
As a function (could be improved):
function checkURLMatch($url, $url_array) {
$url = strtok($url, '?'); // remove everything after ?
if( in_array($url, $url_array)) {
// url exists array
return True;
} else {
// url not in array
return False;
}
}
See it live!
You can use this approach:
function checkURL($me, $s) {
$dir = dirname($me) . '/';
// you may need to refine this
$s = preg_filter(array('~^//~', '~/$~', '~\?.*$~', '~\.\./~'),
array('', '', '', $dir), $s);
// parse resulting URL
$url = parse_url($s);
var_dump($url);
// match parsed URL's path with self
return ($url['path'] === $me);
}
// your page's URL with stripped out .php
$me = str_replace('.php', '', $_SERVER['PHP_SELF']);
// assume this is the URL you are matching against
$s = '../page/';
// compare $me with $s
$ret = checkURL($me, $s);
var_dump($ret);
Live Demo: http://ideone.com/OZZM53
As I have been paid to work on this for the last couple days, I wasn't just sitting around waiting for an answer. I've come up with one that works in my test platform; what does everyone else think? It feels a little bloated, but also feels bulletproof.
Debug echoes left in in case you wanna echo out some stuffs.
global $debug;$debug = false; // toggle debug echoes and var_dumps
/**
* Returns a boolean indicating whether the given URL is the current one.
*
* #param $otherURL the other URL, as a string. Can be any URL, relative or canonical. Invalid URLs will not match.
*
* #return true iff the given URL points to the same place as the current one
*/
function isCurrentURL($otherURL)
{global $debug;
if($debug)echo"<!--\r\nisCurrentURL($otherURL)\r\n{\r\n";
if ($thisURL == $otherURL) // unlikely, but possible. Might as well check.
return true;
// BEGIN Parse other URL
$otherProtocol = parse_url($otherURL);
$otherHost = $otherProtocol["host"] or null; // if $otherProtocol["host"] is set and is not null, use it. Else, use null.
$otherDomain = explode(".", $otherHost) or $otherDomain;
$otherSubdomain = array_shift($otherDomain); // subdom only
$otherDomain = implode(".", $otherDomain); // domain only
$otherFilepath = $otherProtocol["path"] or null;
$otherProtocol = $otherProtocol["scheme"] or null;
// END Parse other URL
// BEGIN Get current URL
#if($debug){echo '$_SERVER == '; var_dump($_SERVER);}
$thisProtocol = $_SERVER["HTTP_X_FORWARDED_PROTO"]; // http or https
$thisHost = $_SERVER["HTTP_HOST"]; // subdom or subdom.domain.tld
$thisDomain = explode(".", $thisHost);
$thisSubdomain = array_shift($thisDomain); // subdom only
$thisDomain = implode(".", $thisDomain); // domain only
if ($thisDomain == "")
$thisDomain = $otherDomain;
$thisFilepath = $_SERVER["PHP_SELF"]; // /path/to/file.php
$thisURL = "$thisProtocol://$thisHost$thisFilepath";
// END Get current URL
if($debug)echo"Current URL is $thisURL ($thisProtocol, $thisSubdomain, $thisDomain, $thisFilepath).\r\n";
if($debug)echo"Other URL is $otherURL ($otherProtocol, $otherHost, $otherFilepath).\r\n";
$thisDomainRegexed = isset($thisDomain) && $thisDomain != null && $thisDomain != "" ? "(\." . str_replace(".","\.",$thisDomain) . ")?" : ""; // prepare domain for insertion into regex
// v this makes the last slash before index.php optional
$regex = "/^(($thisProtocol:)?\/\/$thisSubdomain$thisDomainRegexed)?" . preg_replace('/index\\\..+$/i','?(index\..+)?', str_replace(array(".", "/"), array("\.", "\/"), $thisFilepath)) . '$/i';
if($debug)echo "\r\nregex is $regex\r\nComparing regex against $otherURL";
if (preg_match($regex, $otherURL))
{
if($debug)echo"\r\n\tIt's a match! Returning true...\r\n}\r\n-->";
return true;
}
else
{
if($debug)echo"\r\n\tOther URL is NOT a fully-qualified URL in this subdomain. Checking if it is relative...";
if($otherURL == $thisFilepath) // somewhat likely
{
if($debug)echo"\r\n\t\tOhter URL and this filepath are an exact match! Returning true...\r\n}\r\n-->";
return true;
}
else
{
if($debug)echo"\r\n\t\tFilepath is not an exact match. Testing against regex...";
$regex = regexFilepath($thisFilepath);
if($debug)echo"\r\n\t\tNew Regex is $regex";
if($debug)echo"\r\n\t\tComparing regex against $otherFilepath...";
if (preg_match($regex, $otherFilepath))
{
if($debug)echo"\r\n\t\t\tIt's a match! Returning true...\r\n}\r\n-->";
return true;
}
}
}
if($debug)echo"\r\nI tried my hardest, but couldn't match $otherURL to $thisURL. Returning false...\r\n}\r\n-->";
return false;
}
/**
* Uses the given filepath to create a regex that will match it in any of its relative representations.
*
* #param $path the filepath to be converted
*
* #return a regex that matches a all relative forms of the given filepath
*/
function regexFilepath($path)
{global $debug;
if($debug)echo"\r\nregexFilepath($path)\r\n{\r\n";
$filepathArray = explode("/", $path);
if (count($filepathArray) == 0)
throw new Exception("given parameter not a filepath: $path");
if ($filepathArray[0] == "") // this can happen if the path starts with a "/"
array_shift($filepathArray); // strip the first element off the array
$isIndex = preg_match("/^index\..+$/i", end($filepathArray));
$filename = array_pop($filepathArray);
if($debug){var_dump($filepathArray);}
$ret = '';
foreach($filepathArray as $i)
$ret = "(\.\.\/$ret$i\/)?"; // make a pseudo-recursive relative filepath
if($debug)echo "\r\n$ret";
$ret = preg_replace('/\)\?$/', '?)', $ret); // remove the last '?' and add one before the last '\/'
if($debug)echo "\r\n$ret";
$ret = '/^' . ($ret == '' ? '\.\/' : "((\.\/)|$ret)") . ($isIndex ? '(index\..+)?' : str_replace('.', '\.', $filename)) . '$/i'; // if this filepath leads to an index.php (etc.), then that filename is implied and irrelevant.
if($debug)echo'\r\n}\r\n';
}
This seems to match everything I need it to match, and not what I don't need it to.
I am working on a custom script to automatically send out invites and reminders. I have everything working fine up until a point. My function to send invites looks like this:
function sendInvites($iSurveyID) {
$oSurvey = Survey::model()->findByPk($iSurveyID);
if (!isset($oSurvey)) {
die("could not load survey");
}
if(!tableExists("{{tokens_$iSurveyID}}")) {
die("survey has no tokens or something");
}
$SQLemailstatuscondition = "emailstatus = 'OK'";
$SQLremindercountcondition = '';
$SQLreminderdelaycondition = '';
$iMaxEmails = (int)Yii::app()->getConfig("maxemails");
$iMaxReminders = 1;
if(!is_null($iMaxReminders)) {
$SQLremindercountcondition = "remindercount < " . $iMaxReminders;
}
$oTokens = Tokens_dynamic::model($iSurveyID);
$aResultTokens = $oTokens->findUninvited(false, $iMaxEmails, true, $SQLemailstatuscondition, $SQLremindercountcondition, $SQLreminderdelaycondition);
if (empty($aResultTokens)) {
die("No tokens to send invites to");
}
$aResult = emailTokens($iSurveyID, $aResultTokens, 'invite');
}
I also have a simple little file that starts up Yii:
Yii::createApplication('LSYii_Application', APPPATH . 'config/config' . EXT);
Yii::app()->loadHelper('admin/token');
Yii::app()->loadHelper('common');
Everything works as expected up until I actually try to send emails to the tokens. I've tracked the problem down to the following, on of the functions called by emailTokens has this in it:
$clang = Yii::app()->lang;
$aBasicTokenFields=array('firstname'=>array(
'description'=>$clang->gT('First name'),
'mandatory'=>'N',
'showregister'=>'Y'
),
The Yii::app()->lang part seems to be causing issues because then php is unable to call the gT method. However, when LimeSurvey is running "properly" this never happens. I can't even seem to find where "lang" is in the LimeSurvey source.
What can I do to make it work?
Why do you make it so hard on yourself and not use the RemoteControl2 API ?
See http://manual.limesurvey.org/wiki/RemoteControl_2_API#invite_participants
On that page you will also find a PHP example script.
maybe
Yii::import('application.libraries.Limesurvey_lang');
$clang = new Limesurvey_lang($oTokens->language);