Breaking a PHP array into parts and running chunks asynchronously? - php

I would like to take an array and break it up into chunks (closure $r accomplishes this)
/**
* Breaks an array into bits
*
* #param $list
* #param $p
* #return array
*/
$r = function ($list, $p)
{
$ll = count( $list ); $pl = floor( $ll / $p ); $pt = $ll % $p; $r = []; $m = 0;
for ($px = 0; $px < $p; $px++)
{
$inc = ($px < $pt) ? $pl + 1 : $pl; $r[$px] = array_slice( $list, $m, $inc );
$m += $inc;
}
return $r;
};
$hosts = [
'devhost-0',
'devhost-1',
'devhost-2',
'devhost-3',
'devhost-4',
'devhost-5',
'devhost-6',
'devhost-7',
'devhost-8',
'devhost-9',
'devhost-10',
'devhost-11',
'devhost-12',
'devhost-13',
'devhost-14',
'devhost-15',
'devhost-16',
'devhost-17',
'devhost-18',
'devhost-19',
'devhost-20',
'devhost-21',
'devhost-22',
'devhost-23',
'devhost-24',
'devhost-25',
'devhost-26',
'devhost-27',
'devhost-28',
'devhost-29',
'devhost-30',
'devhost-31',
'devhost-32',
'devhost-33',
'devhost-34',
'devhost-35',
'devhost-36',
];
$hosts = $r($hosts, 6); // chunks of six
This will break the above array into chunks of 5-6, from here I would like to run each of the chunks at the same time
with a simple method, lets say this ping closure below.
/**
* Polls host
*
* #param $host
* #param $port
* #param $timeout
* #return bool
*/
$ping = function ($host, $port, $timeout)
{
$errno = $errstr = false; // silence....
return (! #fSockOpen($host, $port, $errno, $errstr, $timeout))
? false
: true;
};
I'm not sure how to accomplish this though? I would assume that I would be using pcntl_fork() or pthreads but am uncertain as to how I should be setting this up? I have read multiple articles on running processes asynchronously but I am having issues finding one using an array as parts in an example.

i think this could work (based off threading example here http://acm.msu.ru/mkoshp/php-chunked-xhtml/pthreads.tutorials.html )
$hosts = [
'devhost-0',
'devhost-1',
'devhost-2',
'devhost-3',
'devhost-4',
'devhost-5',
'devhost-6',
'devhost-7',
'devhost-8',
'devhost-9',
'devhost-10',
'devhost-11',
'devhost-12',
'devhost-13',
'devhost-14',
'devhost-15',
'devhost-16',
'devhost-17',
'devhost-18',
'devhost-19',
'devhost-20',
'devhost-21',
'devhost-22',
'devhost-23',
'devhost-24',
'devhost-25',
'devhost-26',
'devhost-27',
'devhost-28',
'devhost-29',
'devhost-30',
'devhost-31',
'devhost-32',
'devhost-33',
'devhost-34',
'devhost-35',
'devhost-36',
];
class Devhost_worker extends Thread{
public $jobs;
public function __construct($jobs) {
$this->jobs = $jobs;
}
public function run() {
//do your stuff with the $this->jobs here.
}
}
$chunks=array_chunk($hosts,6,true);
$threads=array();
$i=0;
foreach($chunks as $chunk){
$threads[$i]=new Devhost_worker($chunk);
$threads[$i]->start();
++$i;
}

Something that I came up with just a while ago, will not accept but will post as a possible solution to my question. I was able to ping 4k of my servers in about 1 minute or so.
<?php
define('APP_DIR', dirname(__DIR__)); // autloads in non-sample script....
$hosts = explode("\n", file_get_contents(APP_DIR . '/data/test.txt'));
/**
* Processes in Parallel.
*
* Run a function (with no return result) on each item in an array in parallel.
* Note: This function is only useful if order is not important, and you don't
* need any return values from the function (i.e. no inter-process communication).
*
* #param mixed $func A closure function to apply to each item in parallel.
* #param array $arr The array to apply function to.
* #param integer $procs Number of processes to run in parallel.
*
* #return void
*/
function parallelize($func, array $arr, $procs = 4)
{
$chunks = array_chunk($arr, ceil((count($arr) / $procs)));
$pid = -1;
$children = [];
foreach ($chunks AS $items)
{
$pid = pcntl_fork();
switch ($pid)
{
case (-1): die ('Unable to fork');
case (0): // We are the child process. Pass a chunk of items to process.
array_walk($items, $func);
exit(0);
default: // We are the parent.
$children[] = $pid;
break;
}
}
// Wait for children to finish.
foreach ($children AS $pid)
{
// We are still the parent.
pcntl_waitpid($pid, $status);
}
}
/**
* Polls host
*
* #param $host
* #param $port
* #param $timeout
* #return bool
*/
$ping = function ($host, $port, $timeout)
{
$errno = $errstr = false; // silence....
return (! #fSockOpen($host, $port, $errno, $errstr, $timeout))
? false
: true;
};
/**
* Simple true false return on pinging...
*
* #param $host
* #return bool
*/
$tap = function ($host) USE ($ping)
{
if (! $ping($host, 22, 3))
{
echo "[fail] does not exist\n";
return false;
}
else
{
echo "[good] exists\n";
return true;
}
};
parallelize($tap, $hosts, 20); // do work....

Related

How to wrap functions in PHP's highlight_file with anchor tags?

I found it would be useful for me to be able to have a dumb, read-only version of my (private) code online, so I could check things from my phone without having to open an IDE or SSH into a remote host from a dynamic IP address. I've got all of it figured out except for the last part:
I'm using PHP's highlight_file to get that read-version of the code, but I'd like to make it so clicking on the functions allows me to jump to their declaration, just like CTRL+click does in PhpStorm. In order to do that I wrote up a "find function declaration" script, but now I need to
In order to do that I need to identify what the functions are, then wrap them in an anchor tag to call my function-finding script. So, is there any way I can configure highlight_file to denote my functions so I can wrap them? Or some regex or anything?
For what it's worth, I am using this line-number adding function for my printer.
You might have to re-think the process/requirements. The hightlight_file() function returns alot of HTML injected into the original code i.e.
function test() {}
test();
becomes:
<br />function </span><span style="color: #0000BB">test</span><span style="color: #007700">() {
<br /><span style="color: #0000BB">test</span><span style="color: #007700">();
Just sayin.....
After additional research I found Aidan Lister's comment on the PHP function itself, but the links were 404. After researching him a little I found his Git repo and the relevant functions. Here it is:
https://github.com/aidanlister/code/blob/master/PHP_Highlight.php
And copy/pasted in case a link 404's again:
<?php
/**
* PHP 5 added a set of new constants which need to be declared in this file for
* effective PHP 5 highlighting. It also removed constants, which need to be
* included for PHP 4 highlighting.
*
* The following file will define constants for PHP 4 / PHP 5 compatability
*
* The source of this file can be found at:
* https://raw.githubusercontent.com/aidanlister/code/master/PHP_Highlight.php
*
* It is part of the PEAR PHP_Compat package:
* http://pear.php.net/package/PHP_Compat
*/
require_once 'PHP/Compat/Constant/T.php';
/**
* Improved PHP syntax highlighting.
*
* Generates valid XHTML output with function referencing
* and line numbering.
*
* Extendable output methods provide maximum flexibility,
* toHtml(), toHtmlComment(), toList() and toArray().
*
* Highlighting can be inline (with styles), or the same as
* highlight_file() where colors are taken from php.ini.
*
* #author Aidan Lister <aidan#php.net>
* #version 1.4.3
* #link http://aidanlister.com/repos/v/PHP_Highlight.php
*/
class PHP_Highlight
{
/**
* Hold highlight colors
*
* Contains an associative array of token types and colours.
* By default, it contains the colours as specified by php.ini
*
* For example, to change the colour of strings, use something
* simular to $h->highlight['string'] = 'blue';
*
* #var array
* #access public
*/
var $highlight;
/**
* Things to be replaced for formatting or otherwise reasons
*
* The first element contains the match array, the second the replace
* array.
*
* #var array
* #access public
*/
var $replace = array(
"\t" => ' ',
' ' => ' ');
/**
* Format of the link to the PHP manual page
*
* #var string
* #access public
*/
var $manual = '%s';
/**
* Format of the span tag to be wrapped around each token
*
* #var string
* #access public
*/
var $span;
/**
* Hold the source
*
* #var string
* #access private
*/
var $_source = false;
/**
* Hold plaintext keys
*
* An array of lines which are plaintext
*
* #var array
* #access private
*/
var $_plaintextkeys = array();
/**
* Constructor
*
* Populates highlight array
*
* #param bool $inline If inline styles rather than colors are to be used
* #param bool $plaintext Do not format code outside PHP tags
*/
function PHP_Highlight($inline = false)
{
// Inline
if ($inline === false) {
// Default colours from php.ini
$this->highlight = array(
'string' => ini_get('highlight.string'),
'comment' => ini_get('highlight.comment'),
'keyword' => ini_get('highlight.keyword'),
'bg' => ini_get('highlight.bg'),
'default' => ini_get('highlight.default'),
'html' => ini_get('highlight.html')
);
$this->span = '<span style="color: %s;">%s</span>';
} else {
// Basic styles
$this->highlight = array(
'string' => 'string',
'comment' => 'comment',
'keyword' => 'keyword',
'bg' => 'bg',
'default' => 'default',
'html' => 'html'
);
$this->span = '<span class="%s">%s</span>';
}
}
/**
* Load a file
*
* #access public
* #param string $file The file to load
* #return bool Returns TRUE
*/
function loadFile($file)
{
$this->_source = file_get_contents($file);
return true;
}
/**
* Load a string
*
* #access public
* #param string $string The string to load
* #return bool Returns TRUE
*/
function loadString($string)
{
$this->_source = $string;
return true;
}
/**
* Parse the loaded string into an array
* Source is returned with the element key corresponding to the line number
*
* #access public
* #param bool $funcref Reference functions to the PHP manual
* #param bool $blocks Whether to ignore processing plaintext
* #return array An array of highlighted source code
*/
function toArray($funcref = true, $blocks = false)
{
// Ensure source has been loaded
if ($this->_source == false) {
return false;
}
// Init
$tokens = token_get_all($this->_source);
$manual = $this->manual;
$span = $this->span;
$stringflag = false;
$i = 0;
$out = array();
$out[$i] = '';
// Loop through each token
foreach ($tokens as $j => $token) {
// Single char
if (is_string($token)) {
// Entering or leaving a quoted string
if ($token === '"' && $tokens[$j - 1] !== '\\') {
$stringflag = !$stringflag;
$out[$i] .= sprintf($span, $this->highlight['string'], $token);
} else {
// Skip token2color check for speed
$out[$i] .= sprintf($span, $this->highlight['keyword'], htmlspecialchars($token));
// Heredocs behave strangely
list($tb) = isset($tokens[$j - 1]) ? $tokens[$j - 1] : false;
if ($tb === T_END_HEREDOC) {
$out[++$i] = '';
}
}
continue;
}
// Proper token
list ($token, $value) = $token;
// Make the value safe
$value = htmlspecialchars($value);
$value = str_replace(
array_keys($this->replace),
array_values($this->replace),
$value);
// Process
if ($value === "\n") {
// End this line and start the next
$out[++$i] = '';
} else {
// Function linking
if ($funcref === true && $token === T_STRING) {
// Look ahead 1, look ahead 2, and look behind 3
// For a function we expect T_FUNCTION T_STRING [T_WHITESPACE] (
if ((isset($tokens[$j + 1]) && $tokens[$j + 1] === '(' ||
isset($tokens[$j + 2]) && $tokens[$j + 2] === '(') &&
isset($tokens[$j - 3][0]) && $tokens[$j - 3][0] !== T_FUNCTION
&& function_exists($value)) {
// Insert the manual link
$value = sprintf($manual, $value, $value);
}
}
// Explode token block
$lines = explode("\n", $value);
foreach ($lines as $jj => $line) {
$line = trim($line);
if ($line !== '') {
// Uncomment for debugging
//$out[$i] .= token_name($token);
// Check for plaintext
if ($blocks === true && $token === T_INLINE_HTML) {
$this->_plaintextkeys[] = $i;
$out[$i] .= $line;
} else {
// Highlight encased strings
$colour = ($stringflag === true) ?
$this->highlight['string'] :
$this->_token2color($token);
$out[$i] .= sprintf($span, $colour, $line);
}
}
// Start a new line
if (isset($lines[$jj + 1])) {
$out[++$i] = '';
}
}
}
}
return $out;
}
/**
* Convert the source to an ordered list.
* Each line is wrapped in <li> tags.
*
* #access public
* #param bool $return Return rather than print the results
* #param bool $funcref Reference functions to the PHP manual
* #param bool $blocks Whether to use code blocks around plaintext
* #return string A HTML ordered list
*/
function toList($return = false, $funcref = true, $blocks = true)
{
// Ensure source has been loaded
if ($this->_source == false) {
return false;
}
// Format list
$source = $this->toArray($funcref, $blocks);
$out = "<ol>\n";
foreach ($source as $i => $line) {
$out .= " <li>";
// Some extra juggling for lines which are not code
if (empty($line)) {
$out .= ' ';
} elseif ($blocks === true && in_array($i, $this->_plaintextkeys)) {
$out .= $line;
} else {
$out .= "<code>$line</code>";
}
$out .= "</li>\n";
}
$out .= "</ol>\n";
if ($return === true) {
return $out;
} else {
echo $out;
}
}
/**
* Convert the source to formatted HTML.
* Each line ends with <br />.
*
* #access public
* #param bool $return Return rather than print the results
* #param bool $linenum Display line numbers
* #param string $format Specify format of line numbers displayed
* #param bool $funcref Reference functions to the PHP manual
* #return string A HTML block of code
*/
function toHtml($return = false, $linenum = false, $format = null, $funcref = true)
{
// Ensure source has been loaded
if ($this->_source == false) {
return false;
}
// Line numbering
if ($linenum === true && $format === null) {
$format = '<span>%02d</span> ';
}
// Format code
$source = $this->toArray($funcref);
$out = "<code>\n";
foreach ($source as $i => $line) {
$out .= ' ';
if ($linenum === true) {
$out .= sprintf($format, $i);
}
$out .= empty($line) ? ' ' : $line;
$out .= "<br />\n";
}
$out .= "</code>\n";
if ($return === true) {
return $out;
} else {
echo $out;
}
}
/**
* Convert the source to formatted HTML blocks.
* Each line ends with <br />.
*
* This method ensures only PHP is between <<code>> blocks.
*
* #access public
* #param bool $return Return rather than print the results
* #param bool $linenum Display line numbers
* #param string $format Specify format of line numbers displayed
* #param bool $reset Reset the line numbering each block
* #param bool $funcref Reference functions to the PHP manual
* #return string A HTML block of code
*/
function toHtmlBlocks($return = false, $linenum = false, $format = null, $reset = true, $funcref = true)
{
// Ensure source has been loaded
if ($this->_source == false) {
return false;
}
// Default line numbering
if ($linenum === true && $format === null) {
$format = '<span>%03d</span> ';
}
// Init
$source = $this->toArray($funcref, true);
$out = '';
$wasplain = true;
$k = 0;
// Loop through each line and decide which block to use
foreach ($source as $i => $line) {
// Empty line
if (empty($line)) {
if ($wasplain === true) {
$out .= ' ';
} else {
if (in_array($i+1, $this->_plaintextkeys)) {
$out .= "</code>\n";
// Reset line numbers
if ($reset === true) {
$k = 0;
}
} else {
$out .= ' ';
// Add line number
if ($linenum === true) {
$out .= sprintf($format, ++$k);
}
}
}
// Plain text
} elseif (in_array($i, $this->_plaintextkeys)) {
if ($wasplain === false) {
$out .= "</code>\n";
// Reset line numbers
if ($reset === true) {
$k = 0;
}
}
$wasplain = true;
$out .= str_replace(' ', ' ', $line);
// Code
} else {
if ($wasplain === true) {
$out .= "<code>\n";
}
$wasplain = false;
$out .= ' ';
// Add line number
if ($linenum === true) {
$out .= sprintf($format, ++$k);
}
$out .= $line;
}
$out .= "<br />\n";
}
// Add final code tag
if ($wasplain === false) {
$out .= "</code>\n";
}
// Output method
if ($return === true) {
return $out;
} else {
echo $out;
}
}
/**
* Assign a color based on the name of a token
*
* #access private
* #param int $token The token
* #return string The color of the token
*/
function _token2color($token)
{
switch ($token):
case T_CONSTANT_ENCAPSED_STRING:
return $this->highlight['string'];
break;
case T_INLINE_HTML:
return $this->highlight['html'];
break;
case T_COMMENT:
case T_DOC_COMMENT:
case T_ML_COMMENT:
return $this->highlight['comment'];
break;
case T_ABSTRACT:
case T_ARRAY:
case T_ARRAY_CAST:
case T_AS:
case T_BOOLEAN_AND:
case T_BOOLEAN_OR:
case T_BOOL_CAST:
case T_BREAK:
case T_CASE:
case T_CATCH:
case T_CLASS:
case T_CLONE:
case T_CONCAT_EQUAL:
case T_CONTINUE:
case T_DEFAULT:
case T_DOUBLE_ARROW:
case T_DOUBLE_CAST:
case T_ECHO:
case T_ELSE:
case T_ELSEIF:
case T_EMPTY:
case T_ENDDECLARE:
case T_ENDFOR:
case T_ENDFOREACH:
case T_ENDIF:
case T_ENDSWITCH:
case T_ENDWHILE:
case T_END_HEREDOC:
case T_EXIT:
case T_EXTENDS:
case T_FINAL:
case T_FOREACH:
case T_FUNCTION:
case T_GLOBAL:
case T_IF:
case T_INC:
case T_INCLUDE:
case T_INCLUDE_ONCE:
case T_INSTANCEOF:
case T_INT_CAST:
case T_ISSET:
case T_IS_EQUAL:
case T_IS_IDENTICAL:
case T_IS_NOT_IDENTICAL:
case T_IS_SMALLER_OR_EQUAL:
case T_NEW:
case T_OBJECT_CAST:
case T_OBJECT_OPERATOR:
case T_PAAMAYIM_NEKUDOTAYIM:
case T_PRIVATE:
case T_PROTECTED:
case T_PUBLIC:
case T_REQUIRE:
case T_REQUIRE_ONCE:
case T_RETURN:
case T_SL:
case T_SL_EQUAL:
case T_SR:
case T_SR_EQUAL:
case T_START_HEREDOC:
case T_STATIC:
case T_STRING_CAST:
case T_SWITCH:
case T_THROW:
case T_TRY:
case T_UNSET_CAST:
case T_VAR:
case T_WHILE:
return $this->highlight['keyword'];
break;
case T_CLOSE_TAG:
case T_OPEN_TAG:
case T_OPEN_TAG_WITH_ECHO:
default:
return $this->highlight['default'];
endswitch;
}
}

Undefined index: transfer_encoding

Ok a few weeks ago my script stopped working and today I notice I'm getting an error: [05-Mar-2017 06:31:32 America/Denver] PHP Notice: Undefined index: transfer_encoding in /home2/website/public_html/maps_apps/EasyWebFetch.php on line 105.
Here is what line 105 is:
if ($this->_resp_headers['transfer_encoding'] == 'chunked') {
Can someone point me in the right direction on getting this fixed?
Here is the main code:
<?php
require_once '/home2/website/public_html/maps_apps/EasyWebFetch.php';
$callback = isset($_GET['callback']) ? $_GET['callback'] : 'mymap.weatherhandler';
$station = isset($_GET['rid']) ? $_GET['rid'] : 'FWS';
$product = isset($_GET['product']) ? $_GET['product'] : 'NCR';
$nframes = isset($_GET['frames']) ? $_GET['frames'] : 10;
if (strlen($product) != 3 || strlen($station) != 3) { exit; }
// fetch directory listing
$wf = new EasyWebFetch;
if (!$wf->get("https://radar.weather.gov/ridge/RadarImg/$product/$station/")) {
print $wf->getErrorMessage();
exit;
}
$page = $wf->getContents();
echo $page."\n\n";
$size = preg_match_all( "/href=\"({$station}[\d_]+{$product}\.gif)\"/" , $page, $match);
$files = $match[1];
if ($nframes == 'all') { $nframes = count($files); }
$nframes = min(count($files), $nframes);
$files = array_slice($files, -$nframes);
echo $callback."\n(\n{\ndirectory:\n[\n";
for ($i=0; $i < $nframes; $i++) {
echo "\"ridge/RadarImg/$product/$station/$files[$i]\",\n";
}
echo "]\n}\n)\n;"
?>
and here is EasyWebFetch.php
<?php
/*
* EasyWebFetch - Fetch a page by opening socket connection, no dependencies
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* #author Nashruddin Amin <me#nashruddin.com>
* #copyright Nashruddin Amin 2008
* #license GNU General Public License 3.0
* #package EasyWebFetch
* #version 1.1
*/
class EasyWebFetch
{
private $_request_url;
private $_host;
private $_path;
private $_query;
private $_fragment;
private $_headers_only;
private $_portnum = 80;
private $_user_agent = "SimpleHttpClient/3.0";
private $_req_timeout = 30;
private $_maxredirs = 5;
private $_use_proxy = false;
private $_proxy_host;
private $_proxy_port;
private $_proxy_user;
private $_proxy_pass;
private $_status;
private $_resp_headers;
private $_resp_body;
private $_is_error;
private $_errmsg;
/**
* class constructor
*/
public function __construct()
{
$this->_resp_headers = array();
$this->_resp_body = "";
}
/**
* get the requested page
*
* #param string $url URL of the requested page
* #param boolean $headers_only true to return headers only,
* false to return headers and body
*
* #return boolean true on success, false on failure
*/
public function get($url = '', $headers_only = false)
{
$this->_request_url = $url;
$this->_headers_only = $headers_only;
$redir = 0;
while(($redir++) <= $this->_maxredirs) {
$this->parseUrl($this->_request_url);
if (($response = $this->makeRequest()) == false) {
return(false);
}
/* split head and body */
$neck = strpos($response, "\r\n\r\n");
$head = substr($response, 0, $neck);
$body = substr($response, $neck+2);
/* read response headers */
$this->_resp_headers = $this->parseHeaders($head);
/* check for redirects */
if ($this->getStatus() == 301 || $this->getStatus() == 302) {
$follow = $this->_resp_headers['location'];
$this->_request_url = $this->setFullPath($follow, $this->_request_url);
continue;
} else {
/* no redirects, start reading response body */
break;
}
}
/* read the body part */
if ($this->_resp_headers['transfer_encoding'] == 'chunked') {
$this->_resp_body = $this->joinChunks($body);
} else {
$this->_resp_body = $body;
}
return(true);
}
/**
* build HTTP header and perform HTTP request
*
* #return mixed HTTP response on success, false on failure
*/
private function makeRequest()
{
$method = ($this->_headers_only == true) ? "HEAD" : "GET";
$proxy_auth = base64_encode("$this->_proxy_user:$this->_proxy_pass");
$response = "";
if ($this->_use_proxy) {
$headers = "$method $this->_request_url HTTP/1.1\r\n"
. "Host: $this->_host\r\n"
. "Proxy-Authorization: Basic $proxy_auth\r\n"
. "User-Agent: $this->_user_agent\r\n"
. "Connection: Close\r\n\r\n";
$fp = fsockopen($this->_proxy_host, $this->_proxy_port, $errno, $errmsg, $this->_req_timeout);
} else {
$headers = "$method $this->_path$this->_query$this->_fragment HTTP/1.1\r\n"
. "Host: $this->_host\r\n"
. "User-Agent: $this->_user_agent\r\n"
. "Connection: Close\r\n\r\n";
$fp = fsockopen($this->_host, $this->_portnum, $errno, $errmsg, $this->_req_timeout);
}
if (!$fp) {
$this->_is_error = true;
$this->_errmsg = "Unknown error";
return(false);
}
fwrite($fp, $headers);
while(!feof($fp)) {
$response .= fgets($fp, 4096);
}
fclose($fp);
return($response);
}
/**
* parse the requested URL to its host, path, query and fragment
*
* #return void
*/
private function parseUrl($url)
{
$this->_host = parse_url($url, PHP_URL_HOST);
$this->_path = parse_url($url, PHP_URL_PATH);
$this->_query = parse_url($url, PHP_URL_QUERY);
$this->_fragment = parse_url($url, PHP_URL_FRAGMENT);
if (empty($this->_path)) {
$this->_path = '/';
}
}
/**
* get the full path of the page to redirect. if the requested page is
* http://www.example.com and it redirects to redirpage.html, then the
* new request is http://www.example.com/redirpage.html
*
* #param string $loc new location from the HTTP response headers
* #param string $parent_url the parent's URL
*
* #return string full path of the page to redirect
*/
private function setFullPath($loc, $parent_url)
{
$parent_url = preg_replace("/\/[^\/]*$/", "", $parent_url);
if (strpos($loc, 'http://') !== false) {
return($loc);
}
if (strpos($loc, '../') === false) {
return("$parent_url/$loc");
}
while (strpos($loc, '../') !== false) {
$loc = preg_replace("/^\.\.\//", "", $loc);
$parent_url = preg_replace("/\/[^\/]+$/", "", $parent_url);
}
return("$parent_url/$loc");
}
/**
* parse HTTP response headers to array
*
* #param string $string HTTP response headers
*
* #return array
*/
private function parseHeaders($string)
{
$string = trim($string);
$headers = array();
$lines = explode("\r\n", $string);
$headers['http_status'] = $lines[0];
/* read HTTP _status in first line */
preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $lines[0], $m);
$this->_status = $m[2];
array_splice($lines, 0, 1); /* remove first line */
foreach ($lines as $line) {
list($key, $val) = explode(': ', $line);
$key = str_replace("-", "_", $key);
$key = strtolower($key);
$val = trim($val);
$headers[$key] = $val;
}
return($headers);
}
/**
* join parts of the HTTP response body with chunked transfer-encoding
*
* #param string $chunks HTTP response body
*
* #return string full body
*/
private function joinChunks($chunks)
{
preg_match("/\r\n([0-9a-z]+)(;?.*)\r\n/", $chunks, $match);
$size = hexdec($match[1]);
$body = "";
while($size > 0) {
/* remove line with chunk size */
$chunks = preg_replace("/\r\n.+\r\n/m", "", $chunks, 1);
$part = substr($chunks, 0, $size);
$chunks = substr($chunks, $size);
$body .= $part;
/* get next chunk size */
preg_match("/\r\n([0-9a-z]+)(;?.*)\r\n/", $chunks, $match);
$size = hexdec($match[1]);
}
return($body);
}
/**
* set the requested URL
*
* #param string $url URL of the requested page
*/
public function setRequestUrl($url)
{
$this->_request_url = $url;
}
/**
* set to return headers only
*
* #param boolean $headers_only true to return headers only,
* false to return headers and body
*/
public function returnHeadersOnly($headers_only)
{
$this->_headers_only = $headers_only;
}
/**
* set proxy host and port
*
* #param string $hostport proxy host and proxy port in format proxy_host:proxy_port
*/
public function setProxyHost($hostport)
{
list($this->_proxy_host, $this->_proxy_port) = explode(':', $hostport);
$this->_use_proxy = true;
}
/**
* set proxy user and password
*
* #param string $userpass proxy user and password in format proxy_user:proxy_password
*/
public function setProxyUser($userpass)
{
list($this->_proxy_user, $this->_proxy_pass) = explode(':', $userpass);
}
/**
* get the HTTP response status (200, 404, etc)
*
* #return string
*/
public function getStatus()
{
return($this->_status);
}
/**
* get the requested URL
*
* #return string
*/
public function getRequestUrl()
{
return($this->_request_url);
}
/**
* set maximum redirects
*
* #param int $maxredirs
*/
public function setMaxRedirs($maxredirs)
{
$this->_maxredirs = $maxredirs;
}
/**
* get HTTP response headers
*
* #return array
*/
public function getHeaders()
{
return($this->_resp_headers);
}
/**
* get the HTTP response body, usually in HTML
*
* #return string
*/
public function getContents()
{
return($this->_resp_body);
echo $this->_resp_body;
}
/**
* get error message
*
* #return string
*/
public function getErrorMessage()
{
return($this->_errmsg);
}
/**
* print debug information
*/
private function debug($text)
{
print "$text\n";
}
}
?>
The array _resp_headers doesn't have any element with key transfer_encoding.
To fix the notice, you should check if the array has the key transfer_encoding:
if (array_key_exists('transfer_encoding', $this->_resp_headers) && $this->_resp_headers['transfer_encoding'] == 'chunked') {
But I can't tell you why the key is not set and why the script has stopped working if you don't show more code.
difficult to answer with such a small sample of code but you can check for the index's existence before
if (isset($this->_resp_headers['transfer_encoding']) &&
$this->_resp_headers['transfer_encoding'] == 'chunked') {

Function mod_scorm_insert_scorm_tracks "Invalid parameter"

I have problem with a WebService function for moodle callen "mod_scorm_insert_scorm_tracks"
This function is used for inserting track information (i.e. star time) of a user in his SCORM progress.
Part of the estructure of this function is
scoid= int
attempt= int
tracks[0][element]= string
tracks[0][value]= string
NEW
PHP structe has to look like this
[tracks] =>
Array
(
[0] =>
Array
(
[element] => string
[value] => string
)
)
I have used one of the examples they had in his website everything was fine until I got this error
<b>Notice</b>: Array to string conversion in <b>C:\xampp\htdocs\otros\PHP-REST\curl.php</b> on line <b>247</b><br />
<?xml version="1.0" encoding="UTF-8" ?>
<EXCEPTION class="invalid_parameter_exception">
<ERRORCODE>invalidparameter</ERRORCODE>
<MESSAGE>Invalid parameter value detected</MESSAGE>
<DEBUGINFO>tracks => Invalid parameter value detected: Only arrays accepted. The bad value is: 'Array'</DEBUGINFO>
</EXCEPTION>
And the problem seems to be here:
$item1 = new stdClass();
$item1->scoid = '2';
$item1->attempt = '1';
$item1->tracks = array(
array(
array(
'element' => 'x.start.time',
'value' => '1473102672'
),
),
array(
array(
'element' => 'x.start.time',
'value' => '1473102680'
),
),
);
I tried in many ways
$item1 = new stdClass();
$item1->scoid = '2';
$item1->attempt = '1';
$item1->tracks = array('element' => 'x.start.time','value' => '1473102672');
or
$item1 = new stdClass();
$item1->scoid = '2';
$item1->attempt = '1';
$item1->tracks = array(array ('element' => 'x.start.time','value' => '1473102672'));
And still getting the same message, I'm pretty that is problema with my wyntax but I have tried in many ways and still not working I hope yo can help me.
Complete Code:
/// SETUP - NEED TO BE CHANGED
$token = '481bf3d85a7eb539e37eabc88feccb3c';
$domainname = 'http://localhost/moodle';
//$functionname = 'mod_scorm_launch_sco';
$functionname = 'mod_scorm_insert_scorm_tracks';
//$functionname ='mod_scorm_view_scorm';
// REST RETURNED VALUES FORMAT
$restformat = 'xml'; //Also possible in Moodle 2.2 and later: 'json'
//Setting it to 'json' will fail all calls on earlier Moodle version
$item1 = new stdClass();
$item1->scoid = '2';
$item1->attempt = '1';
$item1->tracks = array(
array(
array(
'element' => 'x.start.time',
'value' => 1473102672
),
),
array(
array(
'element' => 'x.start.time',
'value' => 1473102680
),
),
);
$params = $item1;
/// REST CALL
header('Content-Type: text/plain');
$serverurl = $domainname . '/webservice/rest/server.php'. '?wstoken=' . $token . '&wsfunction='.$functionname;
require_once('./curl.php');
$curl = new curl;
//if rest format == 'xml', then we do not add the param for backward compatibility with Moodle < 2.2
$restformat = ($restformat == 'json')?'&moodlewsrestformat=' . $restformat:'';
$resp = $curl->post($serverurl . $restformat, $params);
print_r($resp);
curl.php
<?php
/**
* cURL class
*
* This is a wrapper class for curl, it is quite easy to use:
* <code>
* $c = new curl;
* // enable cache
* $c = new curl(array('cache'=>true));
* // enable cookie
* $c = new curl(array('cookie'=>true));
* // enable proxy
* $c = new curl(array('proxy'=>true));
*
* // HTTP GET Method
* $html = $c->get('http://example.com');
* // HTTP POST Method
* $html = $c->post('http://example.com/', array('q'=>'words', 'name'=>'moodle'));
* // HTTP PUT Method
* $html = $c->put('http://example.com/', array('file'=>'/var/www/test.txt');
* </code>
*
* #author Dongsheng Cai <dongsheng#moodle.com> - https://github.com/dongsheng/cURL
* #license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
class curl {
/** #var bool */
public $cache = false;
public $proxy = false;
/** #var array */
public $response = array();
public $header = array();
/** #var string */
public $info;
public $error;
/** #var array */
private $options;
/** #var string */
private $proxy_host = '';
private $proxy_auth = '';
private $proxy_type = '';
/** #var bool */
private $debug = false;
private $cookie = false;
private $count = 0;
/**
* #param array $options
*/
public function __construct($options = array()){
if (!function_exists('curl_init')) {
$this->error = 'cURL module must be enabled!';
trigger_error($this->error, E_USER_ERROR);
return false;
}
// the options of curl should be init here.
$this->resetopt();
if (!empty($options['debug'])) {
$this->debug = true;
}
if(!empty($options['cookie'])) {
if($options['cookie'] === true) {
$this->cookie = 'curl_cookie.txt';
} else {
$this->cookie = $options['cookie'];
}
}
if (!empty($options['cache'])) {
if (class_exists('curl_cache')) {
$this->cache = new curl_cache();
}
}
}
/**
* Resets the CURL options that have already been set
*/
public function resetopt(){
$this->options = array();
$this->options['CURLOPT_USERAGENT'] = 'MoodleBot/1.0';
// True to include the header in the output
$this->options['CURLOPT_HEADER'] = 0;
// True to Exclude the body from the output
$this->options['CURLOPT_NOBODY'] = 0;
// TRUE to follow any "Location: " header that the server
// sends as part of the HTTP header (note this is recursive,
// PHP will follow as many "Location: " headers that it is sent,
// unless CURLOPT_MAXREDIRS is set).
//$this->options['CURLOPT_FOLLOWLOCATION'] = 1;
$this->options['CURLOPT_MAXREDIRS'] = 10;
$this->options['CURLOPT_ENCODING'] = '';
// TRUE to return the transfer as a string of the return
// value of curl_exec() instead of outputting it out directly.
$this->options['CURLOPT_RETURNTRANSFER'] = 1;
$this->options['CURLOPT_BINARYTRANSFER'] = 0;
$this->options['CURLOPT_SSL_VERIFYPEER'] = 0;
$this->options['CURLOPT_SSL_VERIFYHOST'] = 2;
$this->options['CURLOPT_CONNECTTIMEOUT'] = 30;
}
/**
* Reset Cookie
*/
public function resetcookie() {
if (!empty($this->cookie)) {
if (is_file($this->cookie)) {
$fp = fopen($this->cookie, 'w');
if (!empty($fp)) {
fwrite($fp, '');
fclose($fp);
}
}
}
}
/**
* Set curl options
*
* #param array $options If array is null, this function will
* reset the options to default value.
*
*/
public function setopt($options = array()) {
if (is_array($options)) {
foreach($options as $name => $val){
if (stripos($name, 'CURLOPT_') === false) {
$name = strtoupper('CURLOPT_'.$name);
}
$this->options[$name] = $val;
}
}
}
/**
* Reset http method
*
*/
public function cleanopt(){
unset($this->options['CURLOPT_HTTPGET']);
unset($this->options['CURLOPT_POST']);
unset($this->options['CURLOPT_POSTFIELDS']);
unset($this->options['CURLOPT_PUT']);
unset($this->options['CURLOPT_INFILE']);
unset($this->options['CURLOPT_INFILESIZE']);
unset($this->options['CURLOPT_CUSTOMREQUEST']);
}
/**
* Set HTTP Request Header
*
* #param array $headers
*
*/
public function setHeader($header) {
if (is_array($header)){
foreach ($header as $v) {
$this->setHeader($v);
}
} else {
$this->header[] = $header;
}
}
/**
* Set HTTP Response Header
*
*/
public function getResponse(){
return $this->response;
}
/**
* private callback function
* Formatting HTTP Response Header
*
* #param mixed $ch Apparently not used
* #param string $header
* #return int The strlen of the header
*/
private function formatHeader($ch, $header)
{
$this->count++;
if (strlen($header) > 2) {
list($key, $value) = explode(" ", rtrim($header, "\r\n"), 2);
$key = rtrim($key, ':');
if (!empty($this->response[$key])) {
if (is_array($this->response[$key])){
$this->response[$key][] = $value;
} else {
$tmp = $this->response[$key];
$this->response[$key] = array();
$this->response[$key][] = $tmp;
$this->response[$key][] = $value;
}
} else {
$this->response[$key] = $value;
}
}
return strlen($header);
}
/**
* Set options for individual curl instance
*
* #param object $curl A curl handle
* #param array $options
* #return object The curl handle
*/
private function apply_opt($curl, $options) {
// Clean up
$this->cleanopt();
// set cookie
if (!empty($this->cookie) || !empty($options['cookie'])) {
$this->setopt(array('cookiejar'=>$this->cookie,
'cookiefile'=>$this->cookie
));
}
// set proxy
if (!empty($this->proxy) || !empty($options['proxy'])) {
$this->setopt($this->proxy);
}
$this->setopt($options);
// reset before set options
curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this,'formatHeader'));
// set headers
if (empty($this->header)){
$this->setHeader(array(
'User-Agent: MoodleBot/1.0',
'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Connection: keep-alive'
));
}
curl_setopt($curl, CURLOPT_HTTPHEADER, $this->header);
if ($this->debug){
echo '<h1>Options</h1>';
var_dump($this->options);
echo '<h1>Header</h1>';
var_dump($this->header);
}
// set options
foreach($this->options as $name => $val) {
if (is_string($name)) {
$name = constant(strtoupper($name));
}
curl_setopt($curl, $name, $val);
}
return $curl;
}
/**
* Download multiple files in parallel
*
* Calls {#link multi()} with specific download headers
*
* <code>
* $c = new curl;
* $c->download(array(
* array('url'=>'http://localhost/', 'file'=>fopen('a', 'wb')),
* array('url'=>'http://localhost/20/', 'file'=>fopen('b', 'wb'))
* ));
* </code>
*
* #param array $requests An array of files to request
* #param array $options An array of options to set
* #return array An array of results
*/
public function download($requests, $options = array()) {
$options['CURLOPT_BINARYTRANSFER'] = 1;
$options['RETURNTRANSFER'] = false;
return $this->multi($requests, $options);
}
/*
* Mulit HTTP Requests
* This function could run multi-requests in parallel.
*
* #param array $requests An array of files to request
* #param array $options An array of options to set
* #return array An array of results
*/
protected function multi($requests, $options = array()) {
$count = count($requests);
$handles = array();
$results = array();
$main = curl_multi_init();
for ($i = 0; $i < $count; $i++) {
$url = $requests[$i];
foreach($url as $n=>$v){
$options[$n] = $url[$n];
}
$handles[$i] = curl_init($url['url']);
$this->apply_opt($handles[$i], $options);
curl_multi_add_handle($main, $handles[$i]);
}
$running = 0;
do {
curl_multi_exec($main, $running);
} while($running > 0);
for ($i = 0; $i < $count; $i++) {
if (!empty($options['CURLOPT_RETURNTRANSFER'])) {
$results[] = true;
} else {
$results[] = curl_multi_getcontent($handles[$i]);
}
curl_multi_remove_handle($main, $handles[$i]);
}
curl_multi_close($main);
return $results;
}
/**
* Single HTTP Request
*
* #param string $url The URL to request
* #param array $options
* #return bool
*/
protected function request($url, $options = array()){
// create curl instance
$curl = curl_init($url);
$options['url'] = $url;
$this->apply_opt($curl, $options);
if ($this->cache && $ret = $this->cache->get($this->options)) {
return $ret;
} else {
$ret = curl_exec($curl);
if ($this->cache) {
$this->cache->set($this->options, $ret);
}
}
$this->info = curl_getinfo($curl);
$this->error = curl_error($curl);
if ($this->debug){
echo '<h1>Return Data</h1>';
var_dump($ret);
echo '<h1>Info</h1>';
var_dump($this->info);
echo '<h1>Error</h1>';
var_dump($this->error);
}
curl_close($curl);
if (empty($this->error)){
return $ret;
} else {
return $this->error;
// exception is not ajax friendly
//throw new moodle_exception($this->error, 'curl');
}
}
/**
* HTTP HEAD method
*
* #see request()
*
* #param string $url
* #param array $options
* #return bool
*/
public function head($url, $options = array()){
$options['CURLOPT_HTTPGET'] = 0;
$options['CURLOPT_HEADER'] = 1;
$options['CURLOPT_NOBODY'] = 1;
return $this->request($url, $options);
}
/**
* Recursive function formating an array in POST parameter
* #param array $arraydata - the array that we are going to format and add into &$data array
* #param string $currentdata - a row of the final postdata array at instant T
* when finish, it's assign to $data under this format: name[keyname][][]...[]='value'
* #param array $data - the final data array containing all POST parameters : 1 row = 1 parameter
*/
function format_array_postdata_for_curlcall($arraydata, $currentdata, &$data) {
foreach ($arraydata as $k=>$v) {
$newcurrentdata = $currentdata;
if (is_object($v)) {
$v = (array) $v;
}
if (is_array($v)) { //the value is an array, call the function recursively
$newcurrentdata = $newcurrentdata.'['.urlencode($k).']';
$this->format_array_postdata_for_curlcall($v, $newcurrentdata, $data);
} else { //add the POST parameter to the $data array
$data[] = $newcurrentdata.'['.urlencode($k).']='.urlencode($v);
}
}
}
/**
* Transform a PHP array into POST parameter
* (see the recursive function format_array_postdata_for_curlcall)
* #param array $postdata
* #return array containing all POST parameters (1 row = 1 POST parameter)
*/
function format_postdata_for_curlcall($postdata) {
if (is_object($postdata)) {
$postdata = (array) $postdata;
}
$data = array();
foreach ($postdata as $k=>$v) {
if (is_object($v)) {
$v = (array) $v;
}
if (is_array($v)) {
$currentdata = urlencode($k);
$this->format_array_postdata_for_curlcall($v, $currentdata, $data);
} else {
$data[] = urlencode($k).'='.urlencode($v);
}
}
$convertedpostdata = implode('&', $data);
return $convertedpostdata;
}
/**
* HTTP POST method
*
* #param string $url
* #param array|string $params
* #param array $options
* #return bool
*/
public function post($url, $params = '', $options = array()){
$options['CURLOPT_POST'] = 1;
if (is_array($params)) {
$params = $this->format_postdata_for_curlcall($params);
}
$options['CURLOPT_POSTFIELDS'] = $params;
return $this->request($url, $options);
}
/**
* HTTP GET method
*
* #param string $url
* #param array $params
* #param array $options
* #return bool
*/
public function get($url, $params = array(), $options = array()){
$options['CURLOPT_HTTPGET'] = 1;
if (!empty($params)){
$url .= (stripos($url, '?') !== false) ? '&' : '?';
$url .= http_build_query($params, '', '&');
}
return $this->request($url, $options);
}
/**
* HTTP PUT method
*
* #param string $url
* #param array $params
* #param array $options
* #return bool
*/
public function put($url, $params = array(), $options = array()){
$file = $params['file'];
if (!is_file($file)){
return null;
}
$fp = fopen($file, 'r');
$size = filesize($file);
$options['CURLOPT_PUT'] = 1;
$options['CURLOPT_INFILESIZE'] = $size;
$options['CURLOPT_INFILE'] = $fp;
if (!isset($this->options['CURLOPT_USERPWD'])){
$this->setopt(array('CURLOPT_USERPWD'=>'anonymous: noreply#moodle.org'));
}
$ret = $this->request($url, $options);
fclose($fp);
return $ret;
}
/**
* HTTP DELETE method
*
* #param string $url
* #param array $params
* #param array $options
* #return bool
*/
public function delete($url, $param = array(), $options = array()){
$options['CURLOPT_CUSTOMREQUEST'] = 'DELETE';
if (!isset($options['CURLOPT_USERPWD'])) {
$options['CURLOPT_USERPWD'] = 'anonymous: noreply#moodle.org';
}
$ret = $this->request($url, $options);
return $ret;
}
/**
* HTTP TRACE method
*
* #param string $url
* #param array $options
* #return bool
*/
public function trace($url, $options = array()){
$options['CURLOPT_CUSTOMREQUEST'] = 'TRACE';
$ret = $this->request($url, $options);
return $ret;
}
/**
* HTTP OPTIONS method
*
* #param string $url
* #param array $options
* #return bool
*/
public function options($url, $options = array()){
$options['CURLOPT_CUSTOMREQUEST'] = 'OPTIONS';
$ret = $this->request($url, $options);
return $ret;
}
public function get_info() {
return $this->info;
}
}
/**
* This class is used by cURL class, use case:
*
* <code>
*
* $c = new curl(array('cache'=>true), 'module_cache'=>'repository');
* $ret = $c->get('http://www.google.com');
* </code>
*
* #package core
* #subpackage file
* #copyright 1999 onwards Martin Dougiamas {#link http://moodle.com}
* #license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class curl_cache {
/** #var string */
public $dir = '';
/**
*
* #param string #module which module is using curl_cache
*
*/
function __construct() {
$this->dir = '/tmp/';
if (!file_exists($this->dir)) {
mkdir($this->dir, 0700, true);
}
$this->ttl = 1200;
}
/**
* Get cached value
*
* #param mixed $param
* #return bool|string
*/
public function get($param){
$this->cleanup($this->ttl);
$filename = 'u_'.md5(serialize($param));
if(file_exists($this->dir.$filename)) {
$lasttime = filemtime($this->dir.$filename);
if(time()-$lasttime > $this->ttl)
{
return false;
} else {
$fp = fopen($this->dir.$filename, 'r');
$size = filesize($this->dir.$filename);
$content = fread($fp, $size);
return unserialize($content);
}
}
return false;
}
/**
* Set cache value
*
* #param mixed $param
* #param mixed $val
*/
public function set($param, $val){
$filename = 'u_'.md5(serialize($param));
$fp = fopen($this->dir.$filename, 'w');
fwrite($fp, serialize($val));
fclose($fp);
}
/**
* Remove cache files
*
* #param int $expire The number os seconds before expiry
*/
public function cleanup($expire){
if($dir = opendir($this->dir)){
while (false !== ($file = readdir($dir))) {
if(!is_dir($file) && $file != '.' && $file != '..') {
$lasttime = #filemtime($this->dir.$file);
if(time() - $lasttime > $expire){
#unlink($this->dir.$file);
}
}
}
}
}
/**
* delete current user's cache file
*
*/
public function refresh(){
if($dir = opendir($this->dir)){
while (false !== ($file = readdir($dir))) {
if(!is_dir($file) && $file != '.' && $file != '..') {
if(strpos($file, 'u_')!==false){
#unlink($this->dir.$file);
}
}
}
}
}
}
Thanks!
Well after some research I finally took plan B
I wrote tracks in a different variable:
$tracks = array();
$tracks[] = array(
'element' => 'cmi.core.lesson_status',
'value' => 'completed'
);
And I followed curl.php array set option:
$arrayName = array('' => , );
Then when I inserted scoid and attemps as single variables in the array:
$params = array('scoid' => '2', 'attempt' => '1', 'tracks' => $tracks);
and boala!the record is on my table:

API for dropbox login, access,upload ,delete using cakephp

I have created application in dropbox developer account.
I am using this component class for dropbox .when i trying to login i am getting this error "Please create your dropbox_token and dropbox_token_secret fields in your user model."
<?php
/**
* CAKEPHP DROPBOX COMPONENT v0.4
* Connects Cakephp to Dropbox using cURL.
*
* Copyright (C) 2010 Kyle Robinson Young
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* #author Kyle Robinson Young <kyle at kyletyoung.com>
* #copyright 2010 Kyle Robinson Young
* #license http://www.opensource.org/licenses/mit-license.php The MIT License
* #version 0.4
* #link http://www.kyletyoung.com/code/cakephp_dropbox_component
*
* SETTINGS:
* email/password: To your dropbox account
* cache: Set to name of cache config or false for no cache
*
* When in doubt, clear the cache.
*
* TODO:
* Make sync function smarter (use modified).
*
*/
class DropboxComponent extends Object
{
var $email, $password;
var $loggedin = false;
var $post, $cookie = array();
var $cache = 'default';
var $_wcache = array();
/**
* INITIALIZE
* #param class $controller
* #param array $settings
*/
function initialize(&$controller, $settings=array())
{
if (!extension_loaded('curl'))
{
trigger_error('Dropbox Component: I require the cURL extension to work.');
} // no curl
if (empty($settings['email']) || empty($settings['password']))
{
trigger_error('Dropbox Component: I need your dropbox email and password to login.');
} // email|pass empty
else
{
$this->email = $settings['email'];
$this->password = $settings['password'];
if (isset($settings['cache'])) $this->cache = $settings['cache'];
$this->login();
} // else
} // initialize
/**
* UPLOAD
* Upload a local file to a remote folder.
*
* #param $file
* #param $dir
* #return bool
*/
function upload($from=null, $to='/')
{
if (!file_exists($from)) return false;
$data = $this->request('https://www.dropbox.com/home');
$token = $this->findOnDropbox('token_upload', $data);
if ($token === false) return false;
$this->post = array(
'plain' => 'yes',
'file' => '#'.$from,
'dest' => $to,
't' => $token
);
$data = $this->request('https://dl-web.dropbox.com/upload');
if (strpos($data, 'HTTP/1.1 302 FOUND') === false) return false;
return true;
} // upload
/**
* DOWNLOAD
* Download a remote file to a local folder.
* Both from and to must be a path to a file name.
*
* #param str $from
* #param str $to
* #param str $w
* #return bool
*/
function download($from=null, $to=null, $w=null)
{
$data = $this->file($from, $w);
if (empty($data['data'])) return false;
if (!is_writable(dirname($to))) return false;
if (!$fp = fopen($to, 'w')) return false;
if (fwrite($fp, $data['data']) === false) return false;
fclose($fp);
return true;
} // download
/**
* SYNC
* Compares files from the local and remote folders
* then syncs them.
* Both local and remote must be folders.
*
* TODO:
* Currently only checks if files exists. Doesn't
* check if they are up to date which it should.
*
* #param str $local
* #param str $remote
* #return bool
*/
function sync($local=null, $remote=null)
{
if (!is_dir($local)) return false;
// GET REMOTE FILES
$remote_files = $this->files($remote);
// GET LOCAL FILES
$local_files = array();
$d = dir($local);
while (false !== ($entry = $d->read()))
{
if (substr($entry, 0, 1) == '.') continue;
if (is_dir($local.DS.$entry)) continue;
$local_files[] = $entry;
} // while
$d->close();
// DOWNLOAD FILES
$tmp = array();
foreach ($remote_files as $file)
{
if (empty($file['w'])) continue;
$tmp[] = $file['name'];
if (in_array($file['name'], $local_files)) continue;
$this->download($file['path'].$file['name'], $local.$file['name'], $file['w']);
} // foreach
// UPLOAD FILES
foreach ($local_files as $file)
{
if (in_array($file, $tmp)) continue;
$this->upload($local.$file, $remote);
} // foreach
return true;
} // sync
/**
* FILES
* Returns an array of remote files/folders
* within the given dir param.
*
* #param str $dir
* #return array
*/
function files($dir='/')
{
$dir = $this->escape($dir);
if ($this->cache === false) Cache::delete('dropbox_files_'.$dir, $this->cache);
if (($files = Cache::read('dropbox_files_'.$dir, $this->cache)) === false)
{
$files = array();
$data = $this->request('https://www.dropbox.com/browse_plain/'.$dir.'?no_js=true');
// GET FILES
$matches = $this->findOnDropbox('files', $data);
if ($matches === false) return false;
// GET TYPES
$types = $this->findOnDropbox('file_types', $data);
// GET SIZES
$sizes = $this->findOnDropbox('file_sizes', $data);
// GET MODS
$mods = $this->findOnDropbox('file_modified_dates', $data);
$i = 0;
foreach ($matches as $key => $file)
{
// IF PARENT
if (strpos($file, "Parent folder") !== false) continue;
// GET FILENAME
$found = $this->findOnDropbox('filename', $file);
if ($found === false) continue;
$found = parse_url($found);
$filename = pathinfo($found['path']);
$filename = $filename['basename'];
if (empty($filename)) continue;
// SET DEFAULTS
$path = $dir.$filename;
$type = 'unknown';
$size = 0;
$modified = 0;
// GET TYPE
if (!empty($types[$key])) $type = trim($types[$key]);
// GET SIZE
if (!empty($sizes[$key])) $size = trim($sizes[$key]);
// GET MODIFIED
if (!empty($mods[$key])) $modified = trim($mods[$key]);
// ADD TO FILES
$files[$i] = array(
'path' => urldecode($dir),
'name' => $filename,
'type' => $type,
'size' => $size,
'modified' => $modified
);
// IF FILE OR FOLDER - FILES HAVE W
$w = $this->findOnDropbox('w', $file);
if ($w !== false)
{
$files[$i]['w'] = $w;
// SAVE W FOR LATER
$this->_wcache[$dir.'/'.$filename] = $w;
} // !empty
$i++;
} // foreach
} // Cache::read
if ($this->cache !== false)
{
Cache::write('dropbox_files_'.$dir, $files, $this->cache);
} // if cache
return $files;
} // files
/**
* FILE
* Returns a remote file as an array.
*
* #param str $file
* #param str $w
* #return array
*/
function file($file=null, $w=null)
{
$file = $this->escape($file);
if ($this->cache === false) Cache::delete('dropbox_file_'.$file, $this->cache);
if (($out = Cache::read('dropbox_file_'.$file, $this->cache)) === false)
{
if (empty($w))
{
if (!empty($this->_wcache[$file])) $w = $this->_wcache[$file];
else return false;
} // empty w
$data = $this->request('https://dl-web.dropbox.com/get/'.$file.'?w='.$w);
$type = $this->findOnDropbox('content_type', $data);
$data = substr(stristr($data, "\r\n\r\n"), 4);
if (!empty($type[0])) $type = $type[0];
$out = array(
'path' => $file,
'w' => $w,
'data' => $data,
'content_type' => $type
);
if ($this->cache !== false)
{
Cache::write('dropbox_file_'.$file, $out, $this->cache);
} // if cache
} // Cache::read
return $out;
} // file
/**
* LOGIN
* to dropbox
*
* #return bool
*/
function login()
{
if (!$this->loggedin)
{
if (empty($this->email) || empty($this->password)) return false;
$data = $this->request('https://www.dropbox.com/login');
// GET TOKEN
$token = $this->findOnDropbox('token_login', $data);
if ($token === false) return false;
// LOGIN TO DROPBOX
$this->post = array(
'login_email' => $this->email,
'login_password' => $this->password,
't' => $token
);
$data = $this->request('https://www.dropbox.com/login');
// IF WERE HOME
if (stripos($data, 'location: /home') === false) return false;
$this->loggedin = true;
} // if loggedin
return true;
} // login
/**
* REQUEST
* Returns data from given url and
* saves cookies. Use $this->post and
* $this->cookie to submit params.
*
* #param str $url
* #return str
*/
function request($url=null)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// IF POST
if (!empty($this->post))
{
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->post);
$this->post = array();
} // !empty
// IF COOKIES
if (!empty($this->cookie))
{
$cookies = array();
foreach ($this->cookie as $key => $val)
{
$cookies[] = "$key=$val";
} // foreach
$cookies = implode(';', $cookies);
curl_setopt($ch, CURLOPT_COOKIE, $cookies);
} // !empty
// GET DATA
$data = curl_exec($ch);
// SAVE COOKIES
$cookies = $this->findOnDropbox('cookies', $data);
if ($cookies !== false)
{
$this->cookie = array_merge($this->cookie, $cookies);
} // if cookies
curl_close($ch);
return $data;
} // request
/**
* ESCAPE
* Returns a dropbox friendly str
* for a url
*
* #param str $str
* #return str
*/
function escape($str=null)
{
return str_replace(
array('+','_','%2E','-','%2F','%3A'),
array('%20','%5F','.','%2D','/',':'),
urlencode($str)
);
} // escape
/**
* FIND ON DROPBOX
* A single function for parsing data from
* Dropbox. For easy update when/if Dropbox
* updates their html.
*
* #param str $key
* #param str $data
* #return mixed
*/
function findOnDropbox($key=null, $data=null)
{
switch (strtolower($key))
{
// FIND FILES & NAMES
case 'files':
preg_match_all('/<div.*details-filename.*>(.*?)<\/div>/i', $data, $matches);
if (!empty($matches[0])) return $matches[0];
break;
// FIND FILE TYPES
case 'file_types':
preg_match_all('/<div.*details-icon.*>(<img.*class="sprite s_(.*)".*>)<\/div>/i', $data, $matches);
if (!empty($matches[2])) return $matches[2];
break;
// FIND FILE SIZES
case 'file_sizes':
preg_match_all('/<div.*details-size.*>(.*)<\/div>/i', $data, $matches);
if (!empty($matches[1])) return $matches[1];
break;
// FIND FILE MODIFIED DATES
case 'file_modified_dates':
preg_match_all('/<div.*details-modified.*>(.*)<\/div>/i', $data, $matches);
if (!empty($matches[1])) return $matches[1];
break;
// FIND FILE NAME
case 'filename':
preg_match('/href=[("|\')]([^("|\')]+)/i', $data, $match);
if (!empty($match[1])) return $match[1];
break;
// FIND W
case 'w':
preg_match('/\?w=(.[^"]*)/i', $data, $match);
if (!empty($match[1])) return $match[1];
break;
// FIND CONTENT TYPE
case 'content_type':
preg_match('/Content-Type: .+\/.+/i', $data, $type);
if (!empty($type)) return $type;
break;
// FIND COOKIES
case 'cookies':
preg_match_all('/Set-Cookie: ([^=]+)=(.*?);/i', $data, $matches);
$return = array();
foreach ($matches[1] as $key => $val)
{
$return[(string)$val] = $matches[2][$key];
} // foreach
if (!empty($return)) return $return;
break;
// FIND LOGIN FORM TOKEN
case 'token_login':
preg_match('/<form [^>]*\/login[^>]*>.*?<\/form>/si', $data, $match);
if (!empty($match[0]))
{
preg_match('/<input [^>]*name="t" [^>]*value="(.*?)"[^>]*>/si', $match[0], $match);
if (!empty($match[1])) return $match[1];
} // !empty
break;
// FIND UPLOAD FORM TOKEN
case 'token_upload':
preg_match('/<form [^>]*https\:\/\/dl-web\.dropbox\.com\/upload[^>]*>.*?<\/form>/si', $data, $match);
if (!empty($match[0]))
{
preg_match('/<input [^>]*name="t" [^>]*value="(.*?)"[^>]*>/si', $match[0], $match);
if (!empty($match[1])) return $match[1];
} // !empty
break;
} // switch
return false;
} // findOnDropbox
} // DropboxComponent
?>
Controller code
class DropboxWebserverController extends AppController
{
var $name = 'DropboxWebserver';
var $uses = array();
var $autoRender = false;
var $components = array('Dropbox' => array(
'email' => 'email#gmail.com',
'password' => 'password',
//'cache' => false
));
var $root_folder = '/';
var $default_home = array('index.html', 'index.htm', 'index.php');
/**
* INDEX
*/
function index()
{
$args = func_get_args();
$args = implode('/', $args);
$path = pathinfo($args);
if ($path['dirname'] == ".")
{
$folder = $path['basename'];
$file = '';
} // dirname == .
else
{
$folder = $path['dirname'];
$file = $path['basename'];
} // else
$files = $this->Dropbox->files($this->root_folder.$folder);
//debug($files);
// FIND FILE
foreach ($files as $f)
{
if (strpos($f['type'], 'folder') !== false) continue;
if (empty($f['name'])) continue;
if ($f['name'] == $file)
{
$file = $this->Dropbox->file($this->root_folder.$folder.'/'.$file, $f['w']);
$output = $file['data'];
$content_type = $file['content_type'];
break;
} // name == file
// FIND DEFAULT HOME
if (in_array($f['name'], $this->default_home))
{
$default = $f;
} // in_array
} // foreach
if (!empty($output))
{
header('Content-Type: '.$content_type);
echo $output;
} // !empty
elseif (!empty($default))
{
$file = $this->Dropbox->file($this->root_folder.$folder.'/'.$default['name'], $default['w']);
header('Content-Type: '.$file['content_type']);
echo $file['data'];
} // !empty default
else
{
echo 'Error 404: File Not Found';
} // else
} // index
}
How to login and access drop box account using cakephp

Vqmod improve error logging, by adding the parent file name the error is referring too

A default error logging using vqmod is as follows:
---------- Date: 2012-10-09 19:46:06 ~ IP : 127.0.0.1 ----------
REQUEST URI : /oc/
MOD DETAILS:
modFile : C:\wamp\www\oc\vqmod\xml\templace.xml
id : Template
version : 1.5.2 - 1.5.2.1
vqmver : 1.0.8
author : templace.com
SEARCH NOT FOUND (ABORTING MOD): require_once(foo . 'library/template.php');
----------------------------------------------------------------------
Example vqmod causing the error
<modification>
<id>Templace</id>
<version>1.5.2 - 1.5.2.1</version>
<author>templace.com</author>
<vqmver>1.0.8</vqmver>
<file name="system/startup.php">
<operation>
<search position="before"><![CDATA[
require_once(foo . 'library/template.php');
]]></search>
<add><![CDATA[
require_once(DIR_SYSTEM . 'library/templace.php');
]]></add>
</operation>
</file>
</modification>
To resole this issue I would have to open the vqmod file templace.xml and search for the file[name] this error is referring too.
QUESTION: How could I add the parent file[name] the actual error is referring too?
E.g adding: "system/startup.php" to the error message to make it easier to debug.
vqmod.php
/**
* VQMod
* #description Main Object used
*/
final class VQMod {
private $_vqversion = '2.1.7';
private $_modFileList = array();
private $_mods = array();
private $_filesModded = array();
private $_cwd = '';
private $_doNotMod = array();
private $_virtualMode = true;
public $useCache = false;
public $logFilePath = 'vqmod/vqmod.log';
public $vqCachePath = 'vqmod/vqcache/';
public $protectedFilelist = 'vqmod/vqprotect.txt';
public $logging = true;
public $cacheTime = 5; // local=5secs live=60secs
public $log;
/**
* VQMod::__construct()
*
* #param bool $path File path to use
* #param bool $logging Enable/disabled logging
* #return null
* #description Startup of VQMod
*/
public function __construct($path = false, $logging = true) {
if(!class_exists('DOMDocument')) {
die('ERROR - YOU NEED DOMDocument INSTALLED TO USE VQMod');
}
if(!$path){
$path = dirname(dirname(__FILE__));
}
$this->_setCwd($path);
$this->logging = (bool) $logging;
$this->log = new VQModLog($this);
$this->_getMods();
$this->_loadProtected();
}
/**
* VQMod::modCheck()
*
* #param string $sourceFile path for file
* #return string
* #description Checks if a file has modifications and applies them, returning cache files or the file name
*/
public function modCheck($sourceFile) {
if(!preg_match('%^([a-z]:)?[\\\\/]%i', $sourceFile)) {
$sourcePath = $this->path($sourceFile);
} else {
$sourcePath = realpath($sourceFile);
}
if(!$sourcePath || is_dir($sourcePath) || in_array($sourcePath, $this->_doNotMod)) {
return $sourceFile;
}
$stripped_filename = preg_replace('~^' . preg_quote($this->getCwd(), '~') . '~', '', $sourcePath);
$cacheFile = $this->_cacheName($stripped_filename);
if($this->useCache && file_exists($cacheFile)) {
//return $cacheFile; // useCache being Deprecated in favor of cacheTime
}
if(isset($this->_filesModded[$sourcePath])) {
return $this->_filesModded[$sourcePath]['cached'] ? $cacheFile : $sourceFile;
}
$changed = false;
$fileHash = sha1_file($sourcePath);
$fileData = file_get_contents($sourcePath);
foreach($this->_mods as $modObject) {
foreach($modObject->mods as $path => $mods) {
if($this->_checkMatch($path, $sourcePath)) {
$modObject->applyMod($mods, $fileData);
}
}
}
// START QPHORIA CACHELOCK CODE
//
if (sha1($fileData) != $fileHash) {
$writePath = $cacheFile;
$cacheLock = false;
if(file_exists($writePath) && ((filemtime($writePath) + (float)$this->cacheTime) >= time())) {
$cacheLock = true;
$changed = true;
}
if(!$cacheLock && (!file_exists($writePath) || is_writable($writePath))) {
file_put_contents($writePath, $fileData);
$changed = true;
} else {
//file_put_contents('./cachelock.txt', "$writePath \r\n", FILE_APPEND); // debugging only.
}
//file_put_contents('./cachetotal.txt', "$writePath \r\n", FILE_APPEND);
} // END QPHORIA CACHELOCK CODE
/* Original Code
if(sha1($fileData) != $fileHash) {
$writePath = $this->_virtualMode ? $cacheFile : $sourcePath;
if(!file_exists($writePath) || is_writable($writePath)) {
file_put_contents($writePath, $fileData);
$changed = true;
}
}*/
$this->_filesModded[$sourcePath] = array('cached' => $changed);
return $changed ? $writePath : $sourcePath;
}
/**
* VQMod::path()
*
* #param string $path File path
* #param bool $skip_real If true path is full not relative
* #return bool, string
* #description Returns the full true path of a file if it exists, otherwise false
*/
public function path($path, $skip_real = false) {
$tmp = $this->_cwd . $path;
$realpath = $skip_real ? $tmp : realpath($tmp);
if(!$realpath) {
return false;
}
if(is_dir($realpath)) {
$realpath = rtrim($realpath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
}
return $realpath;
}
/**
* VQMod::getCwd()
*
* #return string
* #description Returns current working directory
*/
public function getCwd() {
return $this->_cwd;
}
/**
* VQMod::_getMods()
*
* #return null
* #description Gets list of XML files in vqmod xml folder for processing
*/
private function _getMods() {
$this->_modFileList = glob($this->path('vqmod/xml/') . '*.xml');
if($this->_modFileList) {
$this->_parseMods();
} else {
$this->log->write('NO MODS IN USE');
}
}
/**
* VQMod::_parseMods()
*
* #return null
* #description Loops through xml files and attempts to load them as VQModObject's
*/
private function _parseMods() {
$dom = new DOMDocument('1.0', 'UTF-8');
foreach($this->_modFileList as $modFileKey => $modFile) {
if(file_exists($modFile)) {
if(#$dom->load($modFile)) {
$mod = $dom->getElementsByTagName('modification')->item(0);
$this->_mods[] = new VQModObject($mod, $modFile, $this);
} else {
$this->log->write('DOM UNABLE TO LOAD: ' . $modFile);
}
} else {
$this->log->write('FILE NOT FOUND: ' . $modFile);
}
}
}
/**
* VQMod::_loadProtected()
*
* #return null
* #description Loads protected list and adds them to _doNotMod array
*/
private function _loadProtected() {
$file = $this->path($this->protectedFilelist);
if($file && is_file($file)) {
$protected = file_get_contents($file);
if(!empty($protected)) {
$protected = preg_replace('~\r?\n~', "\n", $protected);
$paths = explode("\n", $protected);
foreach($paths as $path) {
$fullPath = $this->path($path);
if($fullPath && !in_array($fullPath, $this->_doNotMod)) {
$this->_doNotMod[] = $fullPath;
}
}
}
}
}
/**
* VQMod::_cacheName()
*
* #param string $file Filename to be converted to cache filename
* #return string
* #description Returns cache file name for a path
*/
private function _cacheName($file) {
return $this->path($this->vqCachePath) . 'vq2-' . preg_replace('~[/\\\\]+~', '_', $file);
}
/**
* VQMod::_setCwd()
*
* #param string $path Path to be used as current working directory
* #return null
* #description Sets the current working directory variable
*/
private function _setCwd($path) {
$realpath = realpath($path);
if(!$realpath) {
die('COULDNT RESOLVE CWD REALPATH');
}
$this->_cwd = rtrim($realpath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
}
/**
* VQMod::_checkMatch()
*
* #param string $modFilePath Modification path from a <file> node
* #param string $checkFilePath File path
* #return bool
* #description Checks a modification path against a file path
*/
private function _checkMatch($modFilePath, $checkFilePath) {
$modFilePath = str_replace('\\', '/', $modFilePath);
$checkFilePath = str_replace('\\', '/', $checkFilePath);
$modFilePath = preg_replace('/([^*]+)/e', 'preg_quote("$1", "~")', $modFilePath);
$modFilePath = str_replace('*', '[^/]*', $modFilePath);
$return = (bool) preg_match('~^' . $modFilePath . '$~', $checkFilePath);
return $return;
}
}
/**
* VQModLog
* #description Object to log information to a file
*/
class VQModLog {
private $_sep;
private $_vqmod;
private $_defhash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';
private $_logs = array();
/**
* VQModLog::__construct()
*
* #param VQMod $vqmod VQMod main class as reference
* #return null
* #description Object instantiation method
*/
public function __construct(VQMod $vqmod) {
$this->_vqmod = $vqmod;
$this->_sep = str_repeat('-', 70);
}
/**
* VQModLog::__destruct()
*
* #return null
* #description Logs any messages to the log file just before object is destroyed
*/
public function __destruct() {
if(empty($this->_logs) || $this->_vqmod->logging == false) {
return;
}
$txt = array();
$txt[] = str_repeat('-', 10) . ' Date: ' . date('Y-m-d H:i:s') . ' ~ IP : ' . (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'N/A') . ' ' . str_repeat('-', 10);
$txt[] = 'REQUEST URI : ' . $_SERVER['REQUEST_URI'];
foreach($this->_logs as $count => $log) {
if($log['obj']) {
$vars = get_object_vars($log['obj']);
$txt[] = 'MOD DETAILS:';
foreach($vars as $k => $v) {
if(is_string($v)) {
$txt[] = ' ' . str_pad($k, 10, ' ', STR_PAD_RIGHT) . ': ' . $v;
}
}
}
foreach($log['log'] as $msg) {
$txt[] = $msg;
}
if ($count > count($this->_logs)-1) {
$txt[] = '';
}
}
$txt[] = $this->_sep;
$txt[] = str_repeat(PHP_EOL, 2);
$logPath = $this->_vqmod->path($this->_vqmod->logFilePath, true);
if(!file_exists($logPath)) {
$res = file_put_contents($logPath, '');
if($res === false) {
die('COULD NOT WRITE TO LOG FILE');
}
}
file_put_contents($logPath, implode(PHP_EOL, $txt), FILE_APPEND);
}
/**
* VQModLog::write()
*
* #param string $data Text to be added to log file
* #param VQModObject $obj Modification the error belongs to
* #return null
* #description Adds error to log object ready to be output
*/
public function write($data, VQModObject $obj = NULL) {
if($obj) {
$hash = sha1($obj->id);
} else {
$hash = $this->_defhash;
}
if(empty($this->_logs[$hash])) {
$this->_logs[$hash] = array(
'obj' => $obj,
'log' => array()
);
}
$this->_logs[$hash]['log'][] = $data;
}
}
/**
* VQModObject
* #description Object for the <modification> that orchestrates each applied modification
*/
class VQModObject {
public $modFile = '';
public $id = '';
public $version = '';
public $vqmver = '';
public $author = '';
public $mods = array();
private $_vqmod;
private $_skip = false;
/**
* VQModObject::__construct()
*
* #param DOMNode $node <modification> node
* #param string $modFile File modification is from
* #param VQMod $vqmod VQMod object as reference
* #return null
* #description Loads modification meta information
*/
public function __construct(DOMNode $node, $modFile, VQmod $vqmod) {
if($node->hasChildNodes()) {
foreach($node->childNodes as $child) {
$name = (string) $child->nodeName;
if(isset($this->$name)) {
$this->$name = (string) $child->nodeValue;
}
}
}
$this->modFile = $modFile;
$this->_vqmod = $vqmod;
$this->_parseMods($node);
}
/**
* VQModObject::skip()
*
* #return bool
* #description Returns the skip status of a modification
*/
public function skip() {
return $this->_skip;
}
/**
* VQModObject::applyMod()
*
* #param array $mods Array of search add nodes
* #param string $data File contents to be altered
* #return null
* #description Applies all modifications to the text data
*/
public function applyMod($mods, &$data) {
if($this->_skip) return;
$tmp = $data;
foreach($mods as $mod) {
$indexCount = 0;
$tmp = $this->_explodeData($tmp);
$lineMax = count($tmp) - 1;
switch($mod['search']->position) {
case 'top':
$tmp[$mod['search']->offset] = $mod['add']->getContent() . $tmp[$mod['search']->offset];
break;
case 'bottom':
$offset = $lineMax - $mod['search']->offset;
if($offset < 0){
$tmp[-1] = $mod['add']->getContent();
} else {
$tmp[$offset] .= $mod['add']->getContent();
}
break;
case 'all':
$tmp = array($mod['add']->getContent());
break;
default:
$changed = false;
foreach($tmp as $lineNum => $line) {
if($mod['search']->regex == 'true') {
$pos = #preg_match($mod['search']->getContent(), $line);
if($pos === false) {
if($mod['error'] == 'log' || $mod['error'] == 'abort' ) {
$this->_vqmod->log->write('INVALID REGEX ERROR - ' . $mod['search']->getContent(), $this);
}
continue 2;
} elseif($pos == 0) {
$pos = false;
}
} else {
$pos = strpos($line, $mod['search']->getContent());
}
if($pos !== false) {
$indexCount++;
$changed = true;
if(!$mod['search']->indexes() || ($mod['search']->indexes() && in_array($indexCount, $mod['search']->indexes()))) {
switch($mod['search']->position) {
case 'before':
$offset = ($lineNum - $mod['search']->offset < 0) ? -1 : $lineNum - $mod['search']->offset;
$tmp[$offset] = empty($tmp[$offset]) ? $mod['add']->getContent() : $mod['add']->getContent() . "\n" . $tmp[$offset];
break;
case 'after':
$offset = ($lineNum + $mod['search']->offset > $lineMax) ? $lineMax : $lineNum + $mod['search']->offset;
$tmp[$offset] = $tmp[$offset] . "\n" . $mod['add']->getContent();
break;
default:
if(!empty($mod['search']->offset)) {
for($i = 1; $i <= $mod['search']->offset; $i++) {
if(isset($tmp[$lineNum + $i])) {
$tmp[$lineNum + $i] = '';
}
}
}
if($mod['search']->regex == 'true') {
$tmp[$lineNum] = preg_replace($mod['search']->getContent(), $mod['add']->getContent(), $line);
} else {
$tmp[$lineNum] = str_replace($mod['search']->getContent(), $mod['add']->getContent(), $line);
}
break;
}
}
}
}
if(!$changed) {
$skip = ($mod['error'] == 'skip' || $mod['error'] == 'log') ? ' (SKIPPED)' : ' (ABORTING MOD)';
if($mod['error'] == 'log' || $mod['error'] == 'abort') {
$this->_vqmod->log->write('SEARCH NOT FOUND' . $skip . ': ' . $mod['search']->getContent(), $this);
}
if($mod['error'] == 'abort') {
$this->_skip = true;
return;
}
}
break;
}
ksort($tmp);
$tmp = $this->_implodeData($tmp);
}
$data = $tmp;
}
/**
* VQModObject::_parseMods()
*
* #param DOMNode $node <modification> node to be parsed
* #return null
* #description Parses modifications in preparation for the applyMod method to work
*/
private function _parseMods(DOMNode $node){
$files = $node->getElementsByTagName('file');
foreach($files as $file) {
$fileToMod = $file->getAttribute('name');
$error = ($file->hasAttribute('error')) ? $file->getAttribute('error') : 'log';
$fullPath = $this->_vqmod->path($fileToMod);
if(!$fullPath){
if(strpos($fileToMod, '*') !== false) {
$fullPath = $this->_vqmod->getCwd() . $fileToMod;
} else {
if ($error == 'log' || $error == 'abort') {
$skip = ($error == 'log') ? ' (SKIPPED)' : ' (ABORTING MOD)';
$this->_vqmod->log->write('Could not resolve path for [' . $fileToMod . ']' . $skip, $this);
}
if ($error == 'log' || $error == 'skip') {
continue;
} elseif ($error == 'abort') {
return false;
}
}
}
$operations = $file->getElementsByTagName('operation');
foreach($operations as $operation) {
$error = ($operation->hasAttribute('error')) ? $operation->getAttribute('error') : 'abort';
$this->mods[$fullPath][] = array(
'search' => new VQSearchNode($operation->getElementsByTagName('search')->item(0)),
'add' => new VQAddNode($operation->getElementsByTagName('add')->item(0)),
'error' => $error
);
}
}
}
/**
* VQModObject::_explodeData()
*
* #param string $data File contents
* #return string
* #description Splits a file into an array of individual lines
*/
private function _explodeData($data) {
return explode("\n", $data);
}
/**
* VQModObject::_implodeData()
*
* #param array $data Array of lines
* #return string
* #description Joins an array of lines back into a text file
*/
private function _implodeData($data) {
return implode("\n", $data);
}
}
/**
* VQNode
* #description Basic node object blueprint
*/
class VQNode {
public $trim = 'false';
private $_content = '';
/**
* VQNode::__construct()
*
* #param DOMNode $node Search/add node
* #return null
* #description Parses the node attributes and sets the node property
*/
public function __construct(DOMNode $node) {
$this->_content = $node->nodeValue;
if($node->hasAttributes()) {
foreach($node->attributes as $attr) {
$name = $attr->nodeName;
if(isset($this->$name)) {
$this->$name = $attr->nodeValue;
}
}
}
}
/**
* VQNode::getContent()
*
* #return string
* #description Returns the content, trimmed if applicable
*/
public function getContent() {
$content = ($this->trim == 'true') ? trim($this->_content) : $this->_content;
return $content;
}
}
/**
* VQSearchNode
* #description Object for the <search> xml tags
*/
class VQSearchNode extends VQNode {
public $position = 'replace';
public $offset = 0;
public $index = 'false';
public $regex = 'false';
public $trim = 'true';
/**
* VQSearchNode::indexes()
*
* #return bool, array
* #description Returns the index values to use the search on, or false if none
*/
public function indexes() {
if($this->index == 'false') {
return false;
}
$tmp = explode(',', $this->index);
foreach($tmp as $k => $v) {
if(!is_int($v)) {
unset($k);
}
}
$tmp = array_unique($tmp);
return empty($tmp) ? false : $tmp;
}
}
/**
* VQAddNode
* #description Object for the <add> xml tags
*/
class VQAddNode extends VQNode {
}
Also couple of other ideas to make debugging even easier:
List any other vqmod files which have previously edited this same file.
This is another common issue where I find when two extensions are editing the same file and the latter is causing the error but it would be useful to know about any other vqmods editing the same file. Yes I suppose I could add error="skip" to everything but dont think this is the best approach to just hide all of the errors, the user should be made aware there is an error...
"Suggested Fix", maybe some smart way you can test what type of error it is.
Contradict what I said above but even at its most basic form you could suggest hiding the error if its not essential. So that anybody can read it and understand how it fix it.
E.g
OPEN: vqmod/xml/templace.xml (line:23)
FIND: <operation>
REPLACE <operation error="skip">
Adding the line number in the XML file the error is coming from. It would be lovely not having to search all of the time and could quickly go to the line number in the vqmod
The issue for the file being edited is certainly one that is way overdue and one I plan on adding in the next release of vQmod. As for the other suggestions
Interesting idea, and one that could certainly be considered. The only problem I see with this is that it would possibly make some log files enormous
This is going to be next to impossible to incorporate
This is impossible without some pretty expensive runtime. The error doesn't lie in the XML as such, so would require re-opening the xml that's been parsed, searching for the line in question line by line and then reporting that. it sounds simple, but you have to remember that xml's can have the same search parameter for multiple operations - so in that situation you'd be no better off than searching the file yourself

Categories