From 6990dc3700efad3207ec6e710124f7ba18891b31 Mon Sep 17 00:00:00 2001 From: merijnvdk Date: Tue, 12 Dec 2017 15:59:45 +0100 Subject: [PATCH] Initial load of 1.0.1 into git Code copied from Web2All svn repo tag 1.0.1. Modified Web2All_ErrorObserver_Email so the default e-mail address is no longer a web2all address. --- LICENSE => LICENSE.txt | 42 +- README.md | 21 +- composer.json | 25 + src/Web2All/Email/Main.class.php | 212 ++ src/Web2All/Email/MimeMail.class.php | 479 ++++ src/Web2All/ErrorObserver/Display.class.php | 303 +++ src/Web2All/ErrorObserver/Email.class.php | 423 ++++ .../ErrorObserver/Email/attachment.tpl | 1 + src/Web2All/ErrorObserver/Email/main.html.tpl | 24 + .../ErrorObserver/Email/main.plain.tpl | 11 + src/Web2All/ErrorObserver/Email/sub.html.tpl | 28 + src/Web2All/ErrorObserver/Email/sub.plain.tpl | 10 + src/Web2All/ErrorObserver/ErrorLog.class.php | 79 + src/Web2All/Manager/Main.class.php | 2118 +++++++++++++++++ src/Web2All/Manager/PrsLogger.class.php | 57 + .../Manager/ClassLoaderNoAutoLoadTest.php | 70 + tests/Web2All/Manager/ClassLoaderTest.php | 88 + .../Manager/ClassLoaderTest/A.class.php | 8 + .../Web2All/Manager/ClassLoaderTest/B.inc.php | 8 + tests/Web2All/Manager/ClassLoaderTest/C.php | 8 + .../Manager/ClassLoaderTest/D.class.php | 8 + tests/Web2All/Manager/ClassLoaderTest/E.php | 9 + tests/Web2All/Manager/ClassLoaderTest/F.php | 9 + .../ClassLoaderTest/TestInterface.class.php | 5 + .../Manager/ClassLoaderTest/noclass.inc.php | 15 + tests/Web2All/Manager/ManagerTest.php | 25 + tests/Web2All/Manager/PluginInitTest.php | 120 + .../Manager/PluginInitTest/A.class.php | 8 + .../PluginInitTest/PluginExt.class.php | 14 + .../PluginInitTest/PluginExtCons.class.php | 32 + .../PluginInitTest/PluginTrait.class.php | 10 + 31 files changed, 4247 insertions(+), 23 deletions(-) rename LICENSE => LICENSE.txt (94%) create mode 100644 composer.json create mode 100644 src/Web2All/Email/Main.class.php create mode 100644 src/Web2All/Email/MimeMail.class.php create mode 100644 src/Web2All/ErrorObserver/Display.class.php create mode 100644 src/Web2All/ErrorObserver/Email.class.php create mode 100644 src/Web2All/ErrorObserver/Email/attachment.tpl create mode 100644 src/Web2All/ErrorObserver/Email/main.html.tpl create mode 100644 src/Web2All/ErrorObserver/Email/main.plain.tpl create mode 100644 src/Web2All/ErrorObserver/Email/sub.html.tpl create mode 100644 src/Web2All/ErrorObserver/Email/sub.plain.tpl create mode 100644 src/Web2All/ErrorObserver/ErrorLog.class.php create mode 100644 src/Web2All/Manager/Main.class.php create mode 100644 src/Web2All/Manager/PrsLogger.class.php create mode 100644 tests/Web2All/Manager/ClassLoaderNoAutoLoadTest.php create mode 100644 tests/Web2All/Manager/ClassLoaderTest.php create mode 100644 tests/Web2All/Manager/ClassLoaderTest/A.class.php create mode 100644 tests/Web2All/Manager/ClassLoaderTest/B.inc.php create mode 100644 tests/Web2All/Manager/ClassLoaderTest/C.php create mode 100644 tests/Web2All/Manager/ClassLoaderTest/D.class.php create mode 100644 tests/Web2All/Manager/ClassLoaderTest/E.php create mode 100644 tests/Web2All/Manager/ClassLoaderTest/F.php create mode 100644 tests/Web2All/Manager/ClassLoaderTest/TestInterface.class.php create mode 100644 tests/Web2All/Manager/ClassLoaderTest/noclass.inc.php create mode 100644 tests/Web2All/Manager/ManagerTest.php create mode 100644 tests/Web2All/Manager/PluginInitTest.php create mode 100644 tests/Web2All/Manager/PluginInitTest/A.class.php create mode 100644 tests/Web2All/Manager/PluginInitTest/PluginExt.class.php create mode 100644 tests/Web2All/Manager/PluginInitTest/PluginExtCons.class.php create mode 100644 tests/Web2All/Manager/PluginInitTest/PluginTrait.class.php diff --git a/LICENSE b/LICENSE.txt similarity index 94% rename from LICENSE rename to LICENSE.txt index 2b0a1b7..d82f2a5 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2017 Web2All B.V. - -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. +MIT License + +Copyright (c) 2007-2017 Web2All B.V. + +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. diff --git a/README.md b/README.md index 9ca1de3..b201308 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,19 @@ -# framework -Web2All framework +# Web2All framework + +This framework is used by almost all Web2All PHP projects. It used to be proprietary software but now it has been released to the public domain under a MIT license. + +The framework is no longer actively maintained. Most likely it is only of interest if you own software created by Web2All B.V. which was built using this framework. + +## What does it do ## + +The base framework handles the following: + +- Sending e-mail +- handling errors and notices (log or e-mail) +- logging +- configuration management +- class loading (or autoloading) + +## License ## + +Web2All framework is open-sourced software licensed under the MIT license ([https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT "license")). diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..1894776 --- /dev/null +++ b/composer.json @@ -0,0 +1,25 @@ +{ + "name": "web2all/framework", + "description": "Web2All framework", + "keywords": ["web2all","framework"], + "license": "MIT", + "authors": [ + { + "name": "Merijn van den Kroonenberg", + "homepage": "https://github.com/merijnvdk", + "role": "Developer" + } + ], + "require": { + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "autoload": { + "classmap": [ + "src/" + ] + } +} + diff --git a/src/Web2All/Email/Main.class.php b/src/Web2All/Email/Main.class.php new file mode 100644 index 0000000..acba00c --- /dev/null +++ b/src/Web2All/Email/Main.class.php @@ -0,0 +1,212 @@ +Web2All->Plugin->Web2All_Email_Main(); + * $mailer->charset = 'utf-8';// optional + * $mailer->send('', '', 'Example subject can contain utf-8', 'plaintext message', null); + * + * @package Web2All_Email + * @name Web2All_Email_Main class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007-2013 Web2All B.V. + * @version 0.3 + * @since 2007-05-30 + */ +class Web2All_Email_Main extends Web2All_Manager_Plugin { + const VERSION = 0.3; + + /** + * Mail config (key value pairs) + * + * @var Array + */ + protected $config; + + /** + * @var Web2All_Email_MimeMail object + */ + public $mimemail; + + public $charset=""; + + /** + * Constructor, initialises Web2All_Email_MimeMail object + * + * @return Web2All_Email_MimeMail Object + */ + public function __construct(Web2All_Manager_Main $web2all) { + if($web2all->DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + $web2all->debugLog("[Start de email Class]"); + } + parent::__construct($web2all); + + // load config + $defaultconfig=array( + // any extra mime headers which need to be added to the mail + // use \n to separate multiple headers, do not use a trailing newline + // by default we add Auto-Submitted: auto-generated, this indicates its a computer generated mail + 'extra_mime_headers' => 'Auto-Submitted: auto-generated' + ); + $requiredconfig=array( + 'send_mail' => true, + 'override_to_address' => true + ); + + $this->Web2All->Config->validateConfig('Web2All_Email_Main',$requiredconfig); + + $this->config=$this->Web2All->Config->makeConfig('Web2All_Email_Main',$defaultconfig); + } + + /** + * Returns version number of this class + * + * @return Double, version number + */ + public function getVersion() { + return (double)self::VERSION; + } + + /** + * @name send, send an e-mail + * + * @param $mailTo String special mail-email format, see below for more info + * @param $mailFrom String special mail-email format, see below for more info + * @param $mailSubject String + * @param $mailText String set to null if you don't want a plaintext part + * @param $mailHTML String set to null if you don't want a html part + * @param $attachments Array array containing assoc array with following keys (for each attachment): + * 'path' : optional path to attachment (including filename) + * 'content': optional content of the file + * 'name' : required attachment name + * 'ctype' : optional content-type of the file + * 'contentid' : optional content id (for inline images) + * + * Special mail-email format; + * The correct email format is "[username] <[email]>" this can result in "Noreply Web2All " + * When using this format the email will be more trusted accepted, this can result in the accepting of external images for example + * + * @return boolean true if sent successfully + */ + public function send($mailTo,$mailFrom,$mailSubject,$mailText,$mailHTML,$attachments=array()) { + // create the mimemail object + $this->buildMime($mailTo,$mailFrom,$mailSubject,$mailText,$mailHTML,$attachments); + + // check if we actually have to send the mail + if($this->config['send_mail']){ + return $this->mimemail->send(); + }else{ + return true; + } + } + + /** + * getRawMail(), get the raw mime mail as a string instead of sending it. + * + * params are the same as the send() method + * + * @param string $mailTo destination e-mail address + * @param string $mailFrom sender e-mail address + * @param string $mailSubject mail subject line + * @param string $mailText plaintext message or null if it has to be left out + * @param string $mailHTML html message or null if it has to be left out + * @param array $attachments optional array of attachments (files), assoc array with following keys (for each attachment): + * 'path' : optional path to attachment (including filename) + * 'content': optional content of the file + * 'name' : required attachment name + * 'ctype' : optional content-type of the file + * 'contentid' : optional content id (for inline images) + * @return string the raw mime mail string + */ + public function getRawMail($mailTo,$mailFrom,$mailSubject,$mailText,$mailHTML,$attachments=array()) { + // create the mimemail object + $this->buildMime($mailTo,$mailFrom,$mailSubject,$mailText,$mailHTML,$attachments); + + return $this->mimemail->buildRawMime(); + } + + /** + * buildMime() + * + * Basically this method prepares the mimemail object, ready to send an email + * but without sending the e-mail itself, so the raw mime can be retrieved + * and sent by other means if needed. + * + * params are the same as the send() method + * + * @param string $mailTo + * @param string $mailFrom + * @param string $mailSubject + * @param string $mailText + * @param string $mailHTML + * @param array $attachments + */ + public function buildMime($mailTo,$mailFrom,$mailSubject,$mailText,$mailHTML,$attachments) { + $this->mimemail = $this->Web2All->Factory->Web2All_Email_MimeMail(); + $this->mimemail->from = $mailFrom; + $this->mimemail->headers = "Errors-To: $mailFrom"; + if($this->config['extra_mime_headers']){ + $this->mimemail->headers .= "\n".$this->config['extra_mime_headers']; + } + // check if we have to override the recipients + if($this->config['override_to_address']){ + $this->mimemail->to = $this->config['override_to_address']; + }else{ + $this->mimemail->to = $mailTo; + } + $this->mimemail->subject = $mailSubject; + foreach ($attachments AS $attachment) { + $file_content = ""; + $file_att_name = ""; + if (array_key_exists("path",$attachment) && file_exists($attachment["path"])) { + // Er is een path opgegeven waar de file staat + $fd = fopen ($attachment["path"], "r"); + $file_content = fread ($fd, filesize ($attachment["path"])); + fclose ($fd); + } + if (array_key_exists("content",$attachment)) { + // De inhoud van de file is meegegeven + $file_content = $attachment["content"]; + } + if ($file_content!="" && array_key_exists("name",$attachment)) { + // attachment default properties (from mimemail class) + $att_ctype="application/octet-stream"; + $att_encode="base64"; + $att_charset=''; + $att_contentid=''; + // and now override the defaults + if(array_key_exists("ctype",$attachment)){ + $att_ctype=$attachment["ctype"]; + } + if(array_key_exists("contentid",$attachment)){ + $att_contentid=$attachment["contentid"]; + } + // and now add the attachment + $this->mimemail->add_attachment($file_content,$attachment["name"], $att_ctype, $att_encode, $att_charset, $att_contentid); + } + } + // only send plaintext or html parts, if they are actually given. + if(!is_null($mailHTML)){ + $this->mimemail->add_alternative_part($mailHTML, "", "text/html", "quoted-printable",$this->charset); + } + if(!is_null($mailText)){ + $this->mimemail->add_alternative_part($mailText, "", "text/plain", "quoted-printable",$this->charset); + } + + // set charset of subject + $this->mimemail->subject_charset=$this->charset; + + } + +} +?> \ No newline at end of file diff --git a/src/Web2All/Email/MimeMail.class.php b/src/Web2All/Email/MimeMail.class.php new file mode 100644 index 0000000..84c8d53 --- /dev/null +++ b/src/Web2All/Email/MimeMail.class.php @@ -0,0 +1,479 @@ + + * Modified by Tobias Ratschiller : + * - General code clean-up + * - separate body- and from-property + * - killed some mostly un-necessary stuff + * Modified by Merijn van den Kroonenberg : + * - code cleanup + * Modified by Merijn van den Kroonenberg : + * - expanded and fixed, added quoted printable suppport + * and multipart alternative. Also possible to add + * Content-id part header (inline images) 2005-02-16 + * encode_qp has been taken from Brent R. Matzelle's + * phpmailer - PHP email class + * Modified by Merijn van den Kroonenberg : + * - 2005-03-04: fixed send() return value + * Modified by Hans Oostendorp : + * - 2007-05-30: port to php5 + * + * Example: + * $mail = new Web2All_Email_MimeMail(); + * $mail->from = $mailFrom; + * $mail->headers = "Errors-To: $mailTo"; + * $mail->to = $mailTo; + * $mail->subject = $mailSubject; + * + * if (file_exists($file_att)) { + * $fd = fopen ($file_att, "r"); + * $file_content = fread ($fd, filesize ($file_att)); + * fclose ($fd); + * $mail->add_attachment($file_content, $file_att_name); + * } + * $mail->add_alternative_part($mailHTML, "", "text/html", "quoted-printable"); + * $mail->add_alternative_part($mailText, "", "text/plain", "quoted-printable"); + * $mail->send(); + * + * Content encodings of 'base64' and 'quoted-printable' are automatically executed on the + * content of parts. All other encodings you need to perform on the content in advance (yourself). + * + * The order in which the parts are added is the inverted order in which they are + * placed in the mail. Note: text/plain must be the first part in the mail + * (so add it last). + */ +class Web2All_Email_MimeMail { + const VERSION = 1.2; + public $parts; + public $to; + public $from; + public $headers; + public $subject; + public $body; + + /** + * Line-Ending characters + * + * This is used on message parts which are 'quoted-printable'. All line endings in + * these message parts will be converted to LE. + * + * Please note this whole class uses \n lineendings everywhere, as sendmail requires + * linux lineendings on mails on its input. (because the message is piped through shell?) + * Sendmail will then convert all \n lineendings to \r\n in order to conform to RFC. + * + * @var string + */ + public $LE; + + /** + * Set this property if the subject is in a non ascii encoding + * + * @var string + */ + public $subject_charset; + + public static $DebugLevel=0; + + /** + * void Web2All_Email_MimeMail() + * class constructor + */ + public function __construct() { + if(self::$DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_HIGH) { + echo "    [ Start de Web2All_Email_MimeMail Class ]
\n"; + } + $this->parts = array(); + $this->altparts = array(); + $this->to = ""; + $this->from = ""; + $this->subject = ""; + $this->body = ""; + $this->headers = ""; + $this->LE = "\n"; + } + + /** + * Returns version number of this class + * + * @return Double, version number + */ + public function getVersion() { + return (double)self::VERSION; + } + + /** + * void add_attachment(string message, [string name], [string ctype], [string encode], [string charset], [string content-id]) + * Add an attachment to the mail object + * + * NOTE: be sure to encode all content types other than base64 and quoted + * printable yourself + */ + public function add_attachment($message, $name = "", $ctype = "application/octet-stream", $encode = "base64", $charset='', $contentid='') { + $this->parts[] = array ( "ctype" => $ctype, + "message" => $message, + "encode" => $encode, + "name" => $name, + "charset" => $charset, + "contentid" => $contentid + ); + } + + /** + * void add_alternative_part(string message, [string name], [string ctype], [string encode], [string charset], [string content-id]) + * Add an alternative part to the mail object + * + * NOTE: be sure to encode all content types other than base64 and + * quoted-printable yourself + */ + public function add_alternative_part($message, $name = "", $ctype = "plain/text", $encode = "8bit", $charset='', $contentid='') { + $this->altparts[] = array ( + "ctype" => $ctype, + "message" => $message, + "encode" => $encode, + "name" => $name, + "charset" => $charset, + "contentid" => $contentid + ); + } + + /** + * void build_message(array part= + * Build message parts of an multipart mail + * + * NOTE: base64 encoding messages will be encoded + * automaticly my the class. all others will not. + */ + private function build_message($part) { + $message = $part[ "message"]; + // #### only base64 encoding supported + // #### other encodings you have to encode yourself + if ($part[ "encode"] == "base64"){ + $message = chunk_split(base64_encode($message)); + }else if ($part[ "encode"] == "quoted-printable"){ + $message = $this->encode_qp($message); + } + return "Content-Type: ".$part[ "ctype"]. + ($part[ "charset"]? "; charset = \"".$part[ "charset"]. "\"" : ""). + ($part[ "name"]? "; name = \"".$part[ "name"]. "\"" : ""). + (empty($part[ "encode"])? "" : "\nContent-Transfer-Encoding: ".$part[ "encode"] ). + (empty($part[ "contentid"])? "" : "\nContent-ID: ".$part[ "contentid"] ). + ((empty($part["contentid"])&&($part["name"]))? "\nContent-Disposition: attachment; filename=\"".$part["name"].'"' : '' ). + "\n\n$message\n"; + } + + /** + * string build_multipart() + * Build a multipart mail + */ + private function build_multipart() { + $boundary = "b".md5(uniqid(time())); + + $multipart = "Content-Type: multipart/mixed; boundary = $boundary\n\nThis is a MIME encoded message.\n"; + + if(count($this->altparts)>0){ + $multipart .= "\n--$boundary\n".$this->build_alternative_multipart(); + } + + for($i = sizeof($this->parts)-1; $i >= 0; $i--) + { + $multipart .= "\n--$boundary\n".$this->build_message($this->parts[$i]); + } + return $multipart.= "\n--"."$boundary"."--"; + } + + /** + * string build_alternative_multipart() + * Build a build_alternative_multipart mail + */ + private function build_alternative_multipart() { + $boundary = "ba".md5(uniqid(time())); + + $multipart = "Content-Type: multipart/alternative; boundary = $boundary\n\n"; + + for($i = sizeof($this->altparts)-1; $i >= 0; $i--) + { + $multipart .= "\n--$boundary\n".$this->build_message($this->altparts[$i]); + } + return $multipart.= "\n--"."$boundary"."--\n"; + } + + /** + * buildRawMime() + * + * This method is used by send(), but can also be called + * directly if you want to send email by other means and need the + * raw mime message as a string. + * + * @param boolean $addAllHeaders + * @return string the raw mime mail + */ + public function buildRawMime($addAllHeaders=true) { + $mime = ""; + if (!empty($this->from)) + $mime .= "From: ".$this->from. "\n"; + if (!empty($this->headers)) + $mime .= $this->headers. "\n"; + if($addAllHeaders){ + // add all headers, even the ones automatically added by the php mail function + $mime .= "To: ".$this->to. "\n"; + $subject = $this->subject; + if ($this->subject_charset) { + $subject=$this->encodeSubject($subject,$this->subject_charset); + } + $mime .= "Subject: ".$subject. "\n"; + } + + if (!empty($this->body)){ + $this->add_attachment($this->body, "", "text/plain"); + } + // lets build the simpliest message possible + if (count($this->parts)==1 && count($this->altparts)==0) { + // single part mail + $mime .= $this->build_message($this->parts[0]); + }elseif (count($this->parts)==0 && count($this->altparts)==1) { + // single part mail + $mime .= $this->build_message($this->altparts[0]); + }elseif (count($this->parts)==0 && count($this->altparts)>0) { + // multi part alternative mail (without attachments) + $mime .= "MIME-Version: 1.0\n".$this->build_alternative_multipart(); + }else{ + // its a multipart mail with attachments + $mime .= "MIME-Version: 1.0\n".$this->build_multipart(); + } + return $mime; + } + + /** + * void send() + * Send the mail (last class-function to be called) + * + * NOTE: if ->body is set it will be base64 encoded + * do not use body in combination with other parts + * its just a lazy shortcut for simple mails + */ + public function send() { + $mime=$this->buildRawMime(false); + + // possibly convert the subject + $subject = $this->subject; + if ($this->subject_charset) { + $subject=$this->encodeSubject($subject,$this->subject_charset); + } + + // workaround on fix for PHP bug #68776 + // we have to split the headers from the body + list($header_str, $body_str) = explode("\n\n",$mime,2); + + if (!empty($this->from)) { + $extra_param = "-f". self::extractMailAddress($this->from); + return mail($this->to, $subject, $body_str, $header_str, $extra_param); + } else { + return mail($this->to, $subject, $body_str, $header_str); + } + } + + /** + * extract the name@domain part from a full e-mail address + * (full name returns name@domain.com) + * + * when unable to extract something between <> it will return + * the original $full_mail + * + * @param string $full_mail + * @return string + */ + public static function extractMailAddress($full_mail) + { + $matches=array(); + if(preg_match('/<([^>]+)>/',$full_mail,$matches)){ + return $matches[1]; + } + return $full_mail; + } + + + /** + * Changes every end of line from CR or LF to $this->LE. Returns string. + * @private + * @returns string + */ + private function fix_eol($str) { + $str = str_replace("\r\n", "\n", $str); + $str = str_replace("\r", "\n", $str); + if($this->LE != "\n"){ + $str = str_replace("\n", $this->LE, $str); + } + return $str; + } + + /** + * Callback for preg_replace_callback replacing every high ascii, + * control and = characters + * + * @param array $matches + * @returns string + */ + public function qp_replace_high_ascii($matches) + { + return '='.sprintf('%02X', ord($matches[1])); + } + + /** + * Callback for preg_replace_callback replacing every spaces + * and tabs when it's the last character on a line + * + * @param array $matches + * @returns string + */ + public function qp_replace_end_whitespace($matches) + { + return '='.sprintf('%02X', ord($matches[1])).$this->LE; + } + + /** + * Encode string to quoted-printable. Returns a string. + * + * @returns string + */ + public function encode_qp ($str) { + $encoded = $this->fix_eol($str); + if (substr($encoded, -2) != $this->LE) + $encoded .= $this->LE; + + // Replace every high ascii, control and = characters + $encoded = preg_replace_callback("/([\001-\010\013\014\016-\037\075\177-\377])/", + array($this,'qp_replace_high_ascii'), $encoded); + // Replace every spaces and tabs when it's the last character on a line + $encoded = preg_replace_callback("/([\011\040])".$this->LE."/", + array($this,'qp_replace_end_whitespace'), $encoded); + + // Maximum line length of 76 characters before CRLF (74 + space + '=') + $encoded = $this->word_wrap($encoded, 74, true); + + return $encoded; + } + + /** + * Wraps message for use with mailers that do not + * automatically perform wrapping and for quoted-printable. + * Original written by philippe. Returns string. + * + * @returns string + */ + public function word_wrap($message, $length, $qp_mode = false) { + if ($qp_mode) + $soft_break = sprintf(" =%s", $this->LE); + else + $soft_break = $this->LE; + + $message = $this->fix_eol($message); + + if (substr($message, -1) == $this->LE) + $message = substr($message, 0, -1); + + $line = explode($this->LE, $message); + $message = ""; + for ($i=0 ;$i < count($line); $i++) + { + $line_part = explode(" ", $line[$i]); + $buf = ""; + for ($e = 0; $e $length)) + { + $space_left = $length - strlen($buf) - 1; + if ($e != 0) + { + if ($space_left > 20) + { + $len = $space_left; + if (substr($word, $len - 1, 1) == "=") + $len--; + elseif (substr($word, $len - 2, 1) == "=") + $len -= 2; + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= " " . $part; + $message .= $buf . sprintf("=%s", $this->LE); + } + else + { + $message .= $buf . $soft_break; + } + $buf = ""; + } + while (strlen($word) > 0) + { + $len = $length; + if (substr($word, $len - 1, 1) == "=") + $len--; + elseif (substr($word, $len - 2, 1) == "=") + $len -= 2; + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) + $message .= $part . sprintf("=%s", $this->LE); + else + $buf = $part; + } + } + else + { + $buf_o = $buf; + if ($e == 0) + $buf .= $word; + else + $buf .= " " . $word; + if (strlen($buf) > $length and $buf_o != "") + { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + } + $message .= $buf . $this->LE; + } + + return ($message); + } + + /** + * The method can be used to encode the subject which is + * in a non ascii encoding. + * + * encoding style: =?charset?Q?the encoded text?= + * + * @param string $input + * @param string $charset eg. UTF-8 or ISO-8859-1 + * @return string the encoded subject + */ + function encodeSubject($input, $charset = 'ISO-8859-1') + { + // set the internal encoding to the encoding of the given input + // this way no conversion is done, because we only need the 'Q' encoding + $old_enc=mb_internal_encoding(); + mb_internal_encoding($charset); + $output=mb_encode_mimeheader($input,$charset, 'Q', $this->LE); + // and when ready, restore the internal encoding again + mb_internal_encoding($old_enc); + return $output; + // below is a fallback mechanism which seems to work also + /* + preg_match_all('/(\\w*[\\x80-\\xFF]+\\w*)/', $input, $matches); + foreach ($matches[1] as $value) { + $replacement = preg_replace('/([\\x80-\\xFF])/e', '"=" . strtoupper(dechex(ord("\\1")))', $value); + $input = str_replace($value, '=?' . $charset . '?Q?' . $replacement . '?=', $input); + } + return $input; + */ + } + + +}; // end of class Web2All_Email_MimeMail + +?> \ No newline at end of file diff --git a/src/Web2All/ErrorObserver/Display.class.php b/src/Web2All/ErrorObserver/Display.class.php new file mode 100644 index 0000000..5ec28c1 --- /dev/null +++ b/src/Web2All/ErrorObserver/Display.class.php @@ -0,0 +1,303 @@ +errorlist) unless there were too many + * and the cutout was activated. In which case this 'real error count' includes ALL errors, + * and not only the ones displayed. + * + * @var int + */ + private $realerrorcount=0; + + /** + * Constructor + */ + public function __construct(Web2All_Manager_Main $web2all) { + if($web2all->DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + $web2all->debugLog("[Start de Web2All_ErrorObserver_Display Class]"); + } + parent::__construct($web2all); + + // init default config + $this->defaultconfig=array(); + + // set the display mode. three different modes: + // COMPAT : this is DEPRECATED, backwards compatibility mode. Now works the same as + // VERBOSE mode! + // VERBOSE: this is the default, All errors matching the severity set in the 'codes' + // config entry, will be displayed fully, including trace. + // ENDUSER: If there were errors matching the severity set in the 'codes' config entry, + // an enduser notice will be given only once, at the end of the page. + $this->defaultconfig['mode'] = 'VERBOSE'; + + // set the bitmask matching all errorcodes that have to be displayed + $this->defaultconfig['codes'] = 0;// by default show no errors + + // Log instantly or log at the end of the script, instantly only works in VERBOSE mode + $this->defaultconfig['instant_output'] = false;// by default log at the end + + // maxerrors defines how many errors will be logged. + // if more than maxerrors happen, they will not be recorded (to save memory and prevent out of memory errors) + // only applies if instant_output==false + $this->defaultconfig['maxerrors'] = 100; + + // default enduser notice + $this->defaultconfig['enduser_notice'] = "Er is een error opgetreden. Excuses voor het ongemak. Probeer later opnieuw."; + + $this->config=$this->Web2All->Config->makeConfig('Web2All_ErrorObserver_Display',$this->defaultconfig); + + // set config + $codes = $this->config['codes']; + eval("\$codes = $codes;"); + $this->codes = $codes; + + } + + /** + * @name onRequestTerminate + * + * Method to use with register_shutdown_function, will start the + * error output. + */ + public function onRequestTerminate() { + try { + $this->displayExceptions(); + } catch (Exception $e) { + error_log('Caught exception during displaying errors: '.$e->getMessage()); + } + } + + /** + * @name update + * @param $Observerable Web2All_Manager_ErrorObserverable + */ + public function update(SplSubject $Observerable) { + $errorobj=$Observerable->getState(); + // check if this error matches our error code bitmask + if ( !($errorobj->getSeverity() & $this->codes) ) { + // we do not need to mail this error, ignore it + return; + } + // register shutdown function if logging at the end of the script + if (!$this->shutdownregistered && !$this->config['instant_output']) { + register_shutdown_function(array($this,"onRequestTerminate")); + $this->shutdownregistered=true; + } + $this->realerrorcount++; + // only store errors while maxerrors is not yet reached or when outputting instantly + if($this->config['instant_output'] || $this->realerrorcount<=$this->config['maxerrors']){ + $this->errorlist[] = $errorobj; + } + // check if we have to instantly output errors + if($this->config['instant_output'] && $this->config['mode']=='VERBOSE'){ + $this->displayExceptions(); + // reset array + $this->errorlist=array(); + } + } + + /** + * @name displayExceptions + * + * Will display all errors currently stored in $this->errorlist + */ + public function displayExceptions() { + $numerrors = 0; + $display_enduser_notice=false; + foreach ($this->errorlist AS $errorobj) { + $exception=$errorobj->getException(); + $numerrors++; + if($this->config['mode']=='COMPAT' || $this->config['mode']=='VERBOSE'){ + // display verbose error report + + $this->debugLog("
");
+        switch($errorobj->getSeverity()){
+            case E_ERROR:               $message="Error";                  break;
+            case E_WARNING:             $message="Warning";                break;
+            case E_PARSE:               $message="Parse Error";            break;
+            case E_NOTICE:              $message="Notice";                 break;
+            case E_CORE_ERROR:          $message="Core Error";             break;
+            case E_CORE_WARNING:        $message="Core Warning";           break;
+            case E_COMPILE_ERROR:       $message="Compile Error";          break;
+            case E_COMPILE_WARNING:     $message="Compile Warning";        break;
+            case E_USER_ERROR:          $message="User Error";             break;
+            case E_USER_WARNING:        $message="User Warning";           break;
+            case E_USER_NOTICE:         $message="User Notice";            break;
+            case E_STRICT:              $message="Strict Notice";          break;
+            case E_RECOVERABLE_ERROR:   $message="Recoverable Error";      break;
+            case 8192:                  $message="Deprecation Warning";    break; // E_DEPRECATED Since PHP 5.3.0
+            case 16384:                 $message="User Deprecation Warning"; break; // E_USER_DEPRECATED Since PHP 5.3.0
+            default:                    $message="Unknown error (".$errorobj->getSeverity().")"; break;
+        }
+        $this->debugLog($message.": ".$this->htmlentitiesIfdebugMethodAcceptsHtml($exception->getMessage())." in ".$exception->getFile()." on line ".$exception->getLine()."");
+        $this->debugLog("Exception class: ".$this->htmlentitiesIfdebugMethodAcceptsHtml(get_class($exception))."");
+        $this->debugLog("Php code: ".$this->htmlentitiesIfdebugMethodAcceptsHtml($this->getLine($exception->getFile(),$exception->getLine()))."");
+        $this->debugLog("Trace:");
+        $i=0;
+        // if exception is caused by trigger_error, use property triggertrace in stead of method getTrace()
+        // getTrace() contains invalid traces when invoked by trigger_error
+        $traces = $exception->getTrace();
+        if ($exception instanceof Web2All_Manager_TriggerException)
+        {
+          if($exception->triggertrace){
+            $traces = $exception->triggertrace;
+          }
+        }
+        if(!$errorobj->getTraceSuppression()){
+          foreach ($traces AS $trace) {
+            $trace_text = '';
+            $trace_text.= "#".$i." ".(array_key_exists('file',$trace) ? $trace['file'] : '')."(".(array_key_exists('line',$trace) ? $trace['line'] : '')."): ";
+            if (array_key_exists('class',$trace) && array_key_exists('type',$trace)) {
+              $trace_text.= $trace['class'].$trace['type'];
+            }
+            if(array_key_exists('function',$trace)){
+              $trace_text.= $trace['function']."(";
+              $komma='';
+              if (array_key_exists('args',$trace) && count($trace['args'])>0) {
+                foreach ($trace['args'] AS $arg) {
+                  if (is_array($arg)) {
+                    $arg='Array('.count($arg).')';
+                  }elseif (is_object($arg)) {
+                    $arg='Object '.get_class($arg);
+                  }elseif (is_bool($arg)) {
+                    $arg=$arg ? 'true' : 'false';
+                  }else{
+                    $arg="'".$arg."'";
+                  }
+                  
+                  $trace_text.= $komma.$arg;
+                  $komma = ', ';
+                }
+              }
+              $trace_text.= ");";
+            }
+            $i++;
+            $this->debugLog($trace_text,true);
+          }
+        }else{
+          $this->debugLog("trace suppressed");
+        }
+        $this->debugLog("
"); + } + if($this->config['mode']=='ENDUSER'){ + // display end user error report + $display_enduser_notice=true; + } + }// end error loop + // see if we hit the curoff, if so inform + if(!$this->config['instant_output'] && $this->realerrorcount>$this->config['maxerrors']){ + $this->debugLog('MAX ERRORS has been reached, only displayed '.$this->config['maxerrors'].' out of '.$this->realerrorcount.' errors'); + } + // see if we have to display end-user message + if ($display_enduser_notice) { + $this->debugLog($this->config['enduser_notice']); + } + } + + /** + * Put $message to debuglog, returns is message is send, false if not + * + * @param string $message + * @param boolean $keeptags when true, tags will never be stripped + * @return boolean + */ + protected function debugLog($message,$keeptags=false) + { + // if debug doesn't accept html, remove it + if (!$this->Web2All->debugMethodAcceptsHtml()) + { + if(!$keeptags){ + $message = strip_tags($message); + } + // do not send empty lines to plain text debugging + if ($message=='') + { + return false; + } + // some basic markup so you can see the output is from the display observer + $message='| '.$message; + } + $this->Web2All->debugLog($message); + return true; + } + + /** + * htmlentities $message if web2all manager debugmethod accepts html + * + * @param string $message + * @return string + */ + protected function htmlentitiesIfdebugMethodAcceptsHtml($message) + { + if ($this->Web2All->debugMethodAcceptsHtml()) + { + return htmlentities($message); + } + return $message; + } + + /** + * getLine returns the requested line from the given file + * + * It will detect zend encoded files + * + * @param string $file full filename and path to file + * @param int $line the line number to return + * @return string + */ + private function getLine($file,$line=1) { + $phpcode = ""; + if (!file_exists($file)) return $phpcode; + $fp = @fopen($file, "rb"); + if (!$fp) return $phpcode; + $i=0; + while (!@feof($fp) && $i<$line) { + if($i==1){ + if(strpos($phpcode,'@Zend')!==false){ + @fclose($fp); + return 'file is Zend encoded, cannot read PHP code'; + } + } + $i++; + $phpcode = fgets($fp, 4096); + } + @fclose($fp); + return trim($phpcode); + } + +} + +?> \ No newline at end of file diff --git a/src/Web2All/ErrorObserver/Email.class.php b/src/Web2All/ErrorObserver/Email.class.php new file mode 100644 index 0000000..bfefead --- /dev/null +++ b/src/Web2All/ErrorObserver/Email.class.php @@ -0,0 +1,423 @@ +errorlist) unless there were too many + * and the cutout was activated. In which case this 'real error count' includes ALL errors, + * and not only the ones displayed in this mail. + * + * @var int + */ + private $realerrorcount=0; + + /* + * These properties store e-mail addresses (set in config) + */ + private $mailto; + private $mailfrom; + + /* + * These properties store template filenames (set in config) + * + * These are special template files which have some 'variables' defined in brackets. + */ + private $attachment_template; + private $sub_template_plain; + private $sub_template_html; + private $main_template_plain; + private $main_template_html; + + protected $config; + protected $defaultconfig; + + /** + * Should be true when an error has been registered and the register_shutdown_function + * has been registered. + * + * @var boolean + */ + protected $shutdownregistered=false; + + /** + * Integer representation for bitwise compare whith occured error + * + * @var int + */ + private $codes; + + /** + * Constructor + */ + public function __construct(Web2All_Manager_Main $web2all) { + if($web2all->DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + $web2all->debugLog("[Start de Web2All_ErrorObserver_Email Class]"); + } + parent::__construct($web2all); + + // init default config + // fixme: this should be local var, not class property + $this->defaultconfig=array(); + + // email templates (placeholders get replaced) + $this->defaultconfig['attachment_template'] = dirname(__FILE__).'/Email/attachment.tpl'; + $this->defaultconfig['sub_template_plain'] = dirname(__FILE__).'/Email/sub.plain.tpl'; + $this->defaultconfig['sub_template_html'] = dirname(__FILE__).'/Email/sub.html.tpl'; + $this->defaultconfig['main_template_plain'] = dirname(__FILE__).'/Email/main.plain.tpl'; + $this->defaultconfig['main_template_html'] = dirname(__FILE__).'/Email/main.html.tpl'; + + $this->defaultconfig['codes'] = 'E_ERROR&E_USER_ERROR'; + + // maxerrors defines how many errors will be logged. + // if more than maxerrors happen, they will not be recorded (to save memory and prevent out of memory errors) + $this->defaultconfig['maxerrors'] = 100; + + // e-mail address is required (where to sent errormail) + // each e-mail address should be the bare address like this: test@example.com + // $requiredconfig=array( + // 'mailfrom' => true, + // 'mailto' => true + // ); + // but don't actually validate so the framework can be used without setting e-mail addresses + // $this->Web2All->Config->validateConfig('Web2All_ErrorObserver_Email',$requiredconfig); + + $this->defaultconfig['mailto'] = 'errormessages@example.com'; + $this->defaultconfig['mailfrom'] = 'errormessages@example.com'; + + // set config + $this->config=$this->Web2All->Config->makeConfig('Web2All_ErrorObserver_Email',$this->defaultconfig); + + // init object based on config + $this->mailto = $this->config['mailto']; + $this->mailfrom = '"'.(array_key_exists('HTTP_HOST',$_SERVER) ? $_SERVER['HTTP_HOST'] : 'shell script').'" <'.$this->config['mailfrom'].'>'; + $codes = $this->config['codes']; + eval("\$codes = $codes;");// this will 'convert' the string to a constant name + $this->codes = $codes; + $this->attachment_template = $this->config['attachment_template']; + $this->sub_template_plain = $this->config['sub_template_plain']; + $this->sub_template_html = $this->config['sub_template_html']; + $this->main_template_plain = $this->config['main_template_plain']; + $this->main_template_html = $this->config['main_template_html']; + + } + + /** + * flush all errors if any + * + * If there are any errors they will be e-mailed and the errorstate will be + * reset. This is useful for long running processes like daemons. + */ + public function flushErrors() { + if(count($this->errorlist)>0){ + // send errors by mail + $this->emailExceptions(); + // reset errors + $this->errorlist=array(); + $this->realerrorcount=0; + } + } + + public function onRequestTerminate() { + try { + $this->emailExceptions(); + } catch (Exception $e) { + error_log('Caught exception during sending error e-mail: '.$e->getMessage()); + } + } + + /** + * @name update + * @param $Observerable Web2All_Manager_ErrorObserverable + */ + public function update( SplSubject $Observerable) { + $errorobj=$Observerable->getState(); + // check if this error matches our error code bitmask + if ( !($errorobj->getSeverity() & $this->codes) ) { + // we do not need to mail this error, ignore it + return; + } + if (!$this->shutdownregistered) { + // we now have at least one e-mail error, so we need to send the error mail on script termination + register_shutdown_function(array($this,"onRequestTerminate")); + $this->shutdownregistered=true; + } + $this->realerrorcount++; + // only store errors while maxerrors is not yet reached + if($this->realerrorcount<=$this->config['maxerrors']){ + $this->errorlist[] = $errorobj; + } + } + + public function emailExceptions() { + $time=date("D j F Y H:i:s"); + + // a basic error report with one error needs 500k memory. A big report with 40 (small) errors needs 1M memory. + $memlim=Web2All_PHP_INI::getBytes(ini_get('memory_limit')); + // check if enough memory available + if($memlim!=-1 && (memory_get_usage()+1000000)>$memlim){ + // upgrade max memory a bit (1M), to make sure we can at least e-mail the errors + error_log('Web2All_ErrorObserver_Email->emailExceptions: low on memory, increasing limit to '.($memlim+1000000)); + ini_set('memory_limit',$memlim+1000000); + } + + $str_attachments = ""; + $attachments[] = array("content"=>$this->get_phpinfo(),"name"=>"phpinfo.html", 'ctype' => 'text/html'); + $sub_plain = ""; + $sub_html = ""; + // $num_errors is the number of errors included in this mail (the realerrorcount could be higher) + $num_errors=0; + foreach ($this->errorlist AS $errorobj) { + $exception=$errorobj->getException(); + + $num_errors++; + $error = ""; + switch($errorobj->getSeverity()){ + case E_ERROR: $error = "Error"; break; + case E_WARNING: $error = "Warning"; break; + case E_PARSE: $error = "Parse Error"; break; + case E_NOTICE: $error = "Notice"; break; + case E_CORE_ERROR: $error = "Core Error"; break; + case E_CORE_WARNING: $error = "Core Warning"; break; + case E_COMPILE_ERROR: $error = "Compile Error"; break; + case E_COMPILE_WARNING: $error = "Compile Warning"; break; + case E_USER_ERROR: $error = "User Error"; break; + case E_USER_WARNING: $error = "User Warning"; break; + case E_USER_NOTICE: $error = "User Notice"; break; + case E_STRICT: $error = "Strict Notice"; break; + case E_RECOVERABLE_ERROR: $error = "Recoverable Error"; break; + case 8192: $error = "Deprecation Warning"; break; // E_DEPRECATED Since PHP 5.3.0 + case 16384: $error = "User Deprecation Warning"; break; // E_USER_DEPRECATED Since PHP 5.3.0 + default: $error = "Unknown error (".$errorobj->getSeverity().")"; break; + } + $trace_text = ""; + $i=0; + // if exception is caused by trigger_error, use property triggertrace in stead of method getTrace() + // getTrace() contains invalid traces when invoked by trigger_error + $traces = $exception->getTrace(); + if ($exception instanceof Web2All_Manager_TriggerException) + { + if($exception->triggertrace){ + $traces = $exception->triggertrace; + } + } + if(!$errorobj->getTraceSuppression()){ + foreach ($traces AS $trace) { + $trace_text .= "#".$i." ".(array_key_exists('file',$trace) ? $trace['file'] : '')."(".(array_key_exists('line',$trace) ? $trace['line'] : '')."): "; + if (array_key_exists('class',$trace) && array_key_exists('type',$trace)) { + $trace_text .= $trace['class'].$trace['type']; + } + if(array_key_exists('function',$trace)){ + $trace_text .= $trace['function']."("; + $komma=''; + if (array_key_exists('args',$trace) && count($trace['args'])>0) { + foreach ($trace['args'] AS $arg) { + if (is_array($arg)) { + $arg='Array('.count($arg).')'; + }elseif (is_object($arg)) { + $arg='Object '.get_class($arg); + }elseif (is_bool($arg)) { + $arg=$arg ? 'true' : 'false'; + }else{ + $arg="'".$arg."'"; + } + + $trace_text .= $komma.$arg; + $komma = ', '; + } + } + $trace_text .= ");\n"; + } + $i++; + } + }else{ + $trace_text.='suppressed'; + } + $trace_text .= "\n"; + $vars_plain = array( "regelnr" => $exception->getLine(), + "errorno" => $exception->getCode(), + "errortype" => $error, + "filename" => $exception->getFile(), + "message" => $exception->getMessage(), + "trace" => $trace_text, + "exception" => get_class($exception), + "phpcode" => $this->getLine($exception->getFile(),$exception->getLine()) + ); + $vars_html = $vars_plain; + $vars_html['message'] = htmlentities($vars_html['message']); + $vars_html['trace'] = htmlentities($vars_html['trace']); + $vars_html['phpcode'] = htmlentities($vars_html['phpcode']); + $sub_plain.= $this->load_template($this->sub_template_plain,$vars_plain); + $sub_html .= $this->load_template($this->sub_template_html,$vars_html); + } + if ($num_errors==0) { + return false; + } + // check if this script is run commandline (cron) or from apache + $website_script=array_key_exists('HTTP_HOST',$_SERVER); + if ($website_script) { + // run from apache + $mailSubject = "Website error".(($this->realerrorcount>1)?'s':'')." (".$this->realerrorcount.") op ".$_SERVER['HTTP_HOST']; + // if only one error + if($num_errors==1 && array_key_exists(0,$this->errorlist)){ + $thisexception=$this->errorlist[0]->getException(); + if($thisexception && $thisexception instanceof Exception){ + $mailSubject .= ' in '.basename($thisexception->getFile()).':'.$thisexception->getLine().' '; + } + } + }else{ + // run from commandline (or cron) + $mailSubject = "PHP shell script error".(($this->realerrorcount>1)?'s':'')." (".$this->realerrorcount.") "; + $hostname=$this->getHostName(); + if(!empty($hostname)){ + $mailSubject .= 'op ['.$hostname.'] '; + } + // append script name + if(array_key_exists('SCRIPT_FILENAME',$_SERVER)){ + $mailSubject .= 'in '.basename($_SERVER['SCRIPT_FILENAME']).' '; + } + } + for ($i=0;$i$attachments[$i]["name"],"nr"=>($i+1)); + $str_attachments .= $this->load_template($this->attachment_template,$vars); + } + $fouten = "is een fout"; + if ($num_errors>1) { + $fouten = "zijn ".$this->realerrorcount.($this->realerrorcount!=$num_errors? ' fouten (waarvan maar '.$num_errors.' in deze mail geregistreerd)' : ' fouten'); + } + $strpath=''; + if ($website_script && array_key_exists('REQUEST_URI',$_SERVER)) { + $strpath=$_SERVER['REQUEST_URI']; + }else{ + $strpath=$_SERVER['SCRIPT_FILENAME']; + } + $vars_plain = array( "website" => ($website_script ? $_SERVER['HTTP_HOST'] : 'shell script'), + "datetime" => $time, + "attachments" => $str_attachments, + "browser" => (array_key_exists('HTTP_USER_AGENT',$_SERVER) ? $_SERVER['HTTP_USER_AGENT'] : 'N/A'), + "path" => $strpath, + "fouten" => $fouten + ); + $vars_html = $vars_plain; + $vars_html['sub'] = $sub_html; + $vars_plain['sub'] = $sub_plain; + $mailHTML = ''; + $mailText = ''; + $mailHTML = $this->load_template($this->main_template_html,$vars_html); + $mailText = $this->load_template($this->main_template_plain,$vars_plain); + $this->Web2All->Plugin->Web2All_Email_Main->send($this->mailto,$this->mailfrom,$mailSubject,$mailText,$mailHTML,$attachments); + } + + /** + * Get the source code line from the script. + * It will detect if a file is zend encoded (in which case the + * source code cannot be retrieved) + * + * might break on files with very long lines (>4096) in which case + * the wrong line might be returned. + * + * @param string $file the full filename and path to the php file + * @param int $line the line number + * @return string code on the given line in the file + */ + private function getLine($file,$line=1) { + $phpcode = ""; + if (!file_exists($file)) return $phpcode; + $fp = @fopen($file, "rb"); + if (!$fp) return $phpcode; + $i=0; + while (!@feof($fp) && $i<$line) { + if($i==1){ + if(strpos($phpcode,'@Zend')!==false){ + @fclose($fp); + return 'file is Zend encoded, cannot read PHP code'; + } + } + $i++; + $phpcode = fgets($fp, 4096); + } + @fclose($fp); + return trim($phpcode); + } + + private function load_template($file,$vars=array()) { + if (!file_exists($file)) { + throw new Exception("Can't find tempate $file",E_USER_ERROR); + } + $handle = fopen($file,"r"); + $contents = fread($handle, filesize($file)); + fclose($handle); + reset($vars); + while(list($var,$value)=each($vars)) { + $contents=str_replace("[".$var."]",$value,$contents); + } + return $contents; + } + + /** + * get the phpinfo information in a string + * + * @return string the full phpinfo html page as a string + */ + private function get_phpinfo() { + ob_start(); + phpinfo(); + $info=ob_get_contents(); + ob_end_clean(); + return $info; + } + + /** + * find the servers hostname + * + * @return string the hostname or empty string if not found + */ + protected function getHostName() { + if (count($_ENV)==0) { + // somtimes on request termination, the $_ENV is empty but getenv works + // check our custom hostname var + if (getenv('W2A_HOSTNAME')!==false) { + return getenv('W2A_HOSTNAME'); + } + // fallback to generic hostname var + if (getenv('HOSTNAME')!==false) { + return getenv('HOSTNAME'); + } + }else{ + // check environment $_ENV + // check our custom hostname var + if(array_key_exists('W2A_HOSTNAME',$_ENV) && !empty($_ENV['W2A_HOSTNAME'])){ + return $_ENV['W2A_HOSTNAME']; + } + // fallback to generic hostname var + if(array_key_exists('HOSTNAME',$_ENV) && !empty($_ENV['HOSTNAME'])){ + return $_ENV['HOSTNAME']; + } + } + // still nothing, theres a small chance its a SERVER var, lets try that + if(array_key_exists('HOSTNAME',$_SERVER)){ + return $_SERVER['HOSTNAME']; + } + return ""; + } +} + +?> \ No newline at end of file diff --git a/src/Web2All/ErrorObserver/Email/attachment.tpl b/src/Web2All/ErrorObserver/Email/attachment.tpl new file mode 100644 index 0000000..cd5eef0 --- /dev/null +++ b/src/Web2All/ErrorObserver/Email/attachment.tpl @@ -0,0 +1 @@ +Attachment [nr]: [name] diff --git a/src/Web2All/ErrorObserver/Email/main.html.tpl b/src/Web2All/ErrorObserver/Email/main.html.tpl new file mode 100644 index 0000000..ba517d1 --- /dev/null +++ b/src/Web2All/ErrorObserver/Email/main.html.tpl @@ -0,0 +1,24 @@ + + + Website error op [website] + + +

Op [datetime] [fouten] opgetreden in website [website].

+[sub] +
+ + + + + + + + + + + + + +
Gebruikte Browser:[browser]
URL/pad:[path]
Attachments:[attachments]
+ + \ No newline at end of file diff --git a/src/Web2All/ErrorObserver/Email/main.plain.tpl b/src/Web2All/ErrorObserver/Email/main.plain.tpl new file mode 100644 index 0000000..2851375 --- /dev/null +++ b/src/Web2All/ErrorObserver/Email/main.plain.tpl @@ -0,0 +1,11 @@ +Op [datetime] [fouten] opgetreden in website [website]. + +[sub] + + +Gebruikte Browser: +[browser] +URL/Pad: +[path] +Attachments: +[attachments] diff --git a/src/Web2All/ErrorObserver/Email/sub.html.tpl b/src/Web2All/ErrorObserver/Email/sub.html.tpl new file mode 100644 index 0000000..7dc743b --- /dev/null +++ b/src/Web2All/ErrorObserver/Email/sub.html.tpl @@ -0,0 +1,28 @@ +
+

Foutmelding in [filename], op regel '[regelnr]'

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Exception class:[exception]
Errornr:[errorno]
Errortype:[errortype]
Php code:[phpcode]
Error message:[message]
Trace:
[trace]
diff --git a/src/Web2All/ErrorObserver/Email/sub.plain.tpl b/src/Web2All/ErrorObserver/Email/sub.plain.tpl new file mode 100644 index 0000000..2fa7370 --- /dev/null +++ b/src/Web2All/ErrorObserver/Email/sub.plain.tpl @@ -0,0 +1,10 @@ + +Foutmelding in [filename], op regel '[regelnr]' +Exception class: [exception] +Errornr: [errorno] +Errortype: [errortype] +Phpcode: [phpcode] +Error message: +[message] +Trace: +[trace] diff --git a/src/Web2All/ErrorObserver/ErrorLog.class.php b/src/Web2All/ErrorObserver/ErrorLog.class.php new file mode 100644 index 0000000..64f276c --- /dev/null +++ b/src/Web2All/ErrorObserver/ErrorLog.class.php @@ -0,0 +1,79 @@ +DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + $web2all->debugLog("[Start de errorObserverErrorLog Class]"); + } + $this->codes=E_ALL; + parent::__construct($web2all); + // set config + if (!is_null($this->config=$this->Web2All->Config->Web2All_ErrorObserver_ErrorLog)) { + if (array_key_exists("codes",$this->config=$this->Web2All->Config->Web2All_ErrorObserver_ErrorLog)) { + $codes = $this->config=$this->Web2All->Config->Web2All_ErrorObserver_ErrorLog['codes']; + eval("\$codes = $codes;"); + $this->codes = $codes; + } + } + } + + public function update(SplSubject $Observerable) { + $this->errorobj = $Observerable->getState(); + if ( !($this->errorobj->getSeverity() & $this->codes) ) { + return false; + } + $this->logException(); + return true; + } + + public function logException() { + $errortxt = ""; + if($this->Web2All->ErrorAddTime){ + $errortxt.=date("Y-m-d H:i:s "); + } + switch($this->errorobj->getSeverity()){ + case E_ERROR: $errortxt.= "Error"; break; + case E_WARNING: $errortxt.= "Warning"; break; + case E_PARSE: $errortxt.= "Parse Error"; break; + case E_NOTICE: $errortxt.= "Notice"; break; + case E_CORE_ERROR: $errortxt.= "Core Error"; break; + case E_CORE_WARNING: $errortxt.= "Core Warning"; break; + case E_COMPILE_ERROR: $errortxt.= "Compile Error"; break; + case E_COMPILE_WARNING: $errortxt.= "Compile Warning"; break; + case E_USER_ERROR: $errortxt.= "User Error"; break; + case E_USER_WARNING: $errortxt.= "User Warning"; break; + case E_USER_NOTICE: $errortxt.= "User Notice"; break; + case E_STRICT: $errortxt.= "Strict Notice"; break; + case E_RECOVERABLE_ERROR: $errortxt.= "Recoverable Error"; break; + case 8192: $errortxt.= "Deprecation Warning"; break; // E_DEPRECATED Since PHP 5.3.0 + case 16384: $errortxt.= "User Deprecation Warning"; break; // E_USER_DEPRECATED Since PHP 5.3.0 + default: $errortxt.= "Unknown error (".$this->errorobj->getSeverity().")"; break; + } + $exception_class=get_class($this->errorobj->getException()); + $errortxt.= ": ".($exception_class=='Web2All_Manager_TriggerException' ? '' :" [$exception_class] " ).$this->errorobj->getException()->getMessage()." (code:".$this->errorobj->getException()->getCode().") in ".$this->errorobj->getException()->getFile()." on line ".$this->errorobj->getException()->getLine()."\n"; + error_log(preg_replace('/\s+/',' ',$errortxt)); + } +} + +?> \ No newline at end of file diff --git a/src/Web2All/Manager/Main.class.php b/src/Web2All/Manager/Main.class.php new file mode 100644 index 0000000..9341f54 --- /dev/null +++ b/src/Web2All/Manager/Main.class.php @@ -0,0 +1,2118 @@ +Web2All; + } + + /** + * Set the Web2All_Manager_Main object + * + * @internal ignore + * @param Web2All_Manager_Main $web2all + */ + public function setWeb2All($web2all) + { + $this->Web2All = $web2all; + } + +} + +/** + * The base class of all Web2All framework classes + * + * @name Web2All_Manager_Plugin class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007 by Web2All + * @version 0.1 + * @since 2007-06-13 + */ +abstract class Web2All_Manager_Plugin implements Web2All_Manager_PluginInterface { + use Web2All_Manager_PluginTrait; + + /** + * constructor + * + * @param Web2All_Manager_Main $web2all + */ + public function __construct(Web2All_Manager_Main $Web2All) { + if($Web2All->DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_HIGH) { + $Web2All->debugLog(get_class($this)."-> [Start de Web2All_Manager_Plugin Class]"); + } + $this->Web2All = $Web2All; + } + + /** + * Destructor (placeholder) + * + * its here, so Web2All_Manager_Plugin derived classes can + * always call a parent destructor + */ + public function __destruct() { + // do nothing + } +} + +/** + * @name Web2All_Manager_Main class + * + * This class is the core of the Web2All PHP framework. + * + * Every PHP script at Web2All will instantiate this class as one of the first + * things it does. There are several ways to do this, but the latest and rec- + * commended way is the following: + * + * require_once(dirname(__FILE__) . '/../include/Web2All/Manager/Main.class.php'); + * $web2all = Web2All_Manager_Main::newInstance(); + * + * Above: this class is included relative to the script location, so this include + * statement also works when called command line. Using the newInstance() method + * to create an instance has a few prequisites: + * - the include file /include/include.php must exist + * - this include file must have the WEB2ALL_CONFIG_CLASS constant defined + * - the WEB2ALL_CONFIG_CLASS constant must contain the classname of a config class + * which extends Web2All_Manager_Config + * + * @package Web2All_Manager + * @author Hans Oostendorp + * @copyright (c) Copyright 2007-2017 by Web2All + * @version 0.2 + * @since 2007-05-16 + */ +class Web2All_Manager_Main { + /** + * Version: OBSOLETE + * + * The version is not actually used and never updated. Do not use. + */ + const VERSION = 0.2; + + /** + * Debug levels [0-5] + * From no debug logging (0) to full logging (5) + * + */ + const DEBUGLEVEL_NONE = 0; + const DEBUGLEVEL_MINIMAL = 1; + const DEBUGLEVEL_LOW = 2; + const DEBUGLEVEL_MEDIUM = 3; + const DEBUGLEVEL_HIGH = 4; + const DEBUGLEVEL_FULL = 5; + + /** + * Array of root directories for the framework. + * + * Each directory will be searched by the autoloader for classes. + * + * @var array + */ + protected static $frameworkRoots=array(); + + /** + * Was the autoloader added + * + * @var boolean + */ + protected static $autoloaderAdded=false; + + /** + * Observerable who handles list of obsersers that should be notified when error occures + */ + private $errorObserverable; + + /** + * Sets if plugin loader should only include class php file from plugin + * directory, of should also check enviroment path if plugin does not exists + */ + private $pluginloaderChecksEnviromentPath = false; + + /** + * Global storage voor pointers naar classes die automatisch door de + * Web2All_Manager_Main class aangemaakt worden. De global storage is een + * singelton registry class zodat alle classes die de Web2All_Manager_Main + * class extenden deze global storage delen. + * Voorbeeld1: Initialiseer class test voor global gebruik. + * $Web2All->test(); + * De pointer naar class test wordt in de global_data opgeslagen. + * Vanaf nu kunnen alle classes die de Web2All_Manager_Main + * class extenden gebruik maken van $Web2All->test + */ + private $global_data; + + /** + * The default config array for this class + * + * @var Array + */ + protected $DefaultConfig=array(); + + /** + * The config array for this class + * + * @var Array + */ + protected $ManagerConfig=Array(); + + /** + * Local Web2All_Manager_Plugin Access + * + * This property is used to autoload (instantiate) an + * Plugin object. The returned object is always new and not + * singleton. + * + * Use Factory instead of Plugin where possible. You only need + * Plugin when the class does not extend Web2All_Manager_Plugin + * but still requires the Web2All_Manager_Main object as first + * constructor param. + * + * @var Web2All_Manager_PluginLoaderPrivate + */ + public $Plugin; + + /** + * Global Web2All_Manager_Plugin Access + * + * This property is used to autoload (instantiate) an + * Plugin object. The returned object is shared and singleton + * when created through this property. A second call on the same class + * will return a pointer to the existing object. + * + * Use of PluginGlobal is discouraged, sopport might be dropped in the + * future. + * + * @var Web2All_Manager_PluginLoaderGlobal + */ + public $PluginGlobal; + + /** + * Global Web2All_Manager_ClassInclude Access + * This property is used to autoload and not (instantiate) an + * object. + * + * @var Web2All_Manager_ClassInclude + */ + public $ClassInclude; + + /** + * Factory Access to all web2all classes + * + * This property will create an instance of the required class. + * Classfiles will be automatically included. + * + * @var Web2All_Manager_Factory + */ + public $Factory; + + /** + * The config object used by this web2all class + * + * @var Web2All_Manager_Config or extension hereof + */ + public $Config; + + /** + * Number of includes + * + * @var int + */ + public $includes; + + /** + * The debug method for debugLog() + * + * when outputting debuglogs use the following method (echo|echoplain|error_log), default echo + * + * @var string + */ + public $DebugMethod='echo'; + + /** + * The debug level [0-5] where 0 is no debugging + * + * @var int + */ + public $DebugLevel=0; + + /** + * Set to true if datetime has to be prepended to log message + * + * @var boolean + */ + public $DebugAddTime=false; + + /** + * Set to true if debugLog should recognise multiline messages + * + * Will allow putting each line in the message in its own lone in the error + * log if logging to error_log. or will add DateTime to each line if + * DebugAddTime is true and DebugMethod is echo(plain). + * + * @var boolean + */ + public $DebugMultiLine=true; + + /** + * Set to true if datetime has to be prepended to error(log) message + * + * @var boolean + */ + public $ErrorAddTime=false; + + /** + * The User CPU usage when this class is started. + * + * @var integer + */ + public $startUserCPU=0; + + /** + * The System CPU usage when this class is started. + * + * @var integer + */ + public $startSystemCPU=0; + + /** + * Constructor + * Initialise everything that Web2All_Manager_Main always needs + * + * @param Web2All_Manager_Config $config + * @param int $debuglevel [optional int [0-5] set when debugging core Web2All_Manager functionality] + */ + public function __construct(Web2All_Manager_Config $config = null, $debuglevel = 0) { + // set initial debuglevel. + // this is only set when debugging Web2All_Manager core functionality which is + // loaded before config settings are available. + $this->DebugLevel=$debuglevel; + + if($this->DebugLevel >= self::DEBUGLEVEL_HIGH) { + $this->debugLog(get_class($this)."-> [Start de Web2All_Manager_Main Class]"); + if(!is_null($config)){ + $this->debugLog( get_class($this)."-> [using config ".get_class($config)."]"); + } + } + + // record the initial CPU usage + // not portable to windows + $dat = getrusage(); + $this->startUserCPU=$dat["ru_utime.tv_sec"]*1e6+$dat["ru_utime.tv_usec"]; + $this->startSystemCPU=$dat["ru_stime.tv_sec"]*1e6+$dat["ru_stime.tv_usec"]; + + /* + * initiate storages + */ + Web2All_Manager_Registry::$DebugLevel=$this->DebugLevel; + $this->global_data = Web2All_Manager_Registry::getInstance(); + + /* + * initiate Web2All_Manager_Plugin Loaders + */ + $this->Plugin = new Web2All_Manager_PluginLoaderPrivate($this); + $this->PluginGlobal = new Web2All_Manager_PluginLoaderGlobal($this); + $this->Factory = new Web2All_Manager_Factory($this); + $this->ClassInclude = new Web2All_Manager_ClassInclude($this); + + /* + * initiate config + * the config is only local accessable + */ + if(is_null($config)){ + $config = new Web2All_Manager_Config; + } + if (!(is_subclass_of($config,'Web2All_Manager_Config') || (get_class($config)=='Web2All_Manager_Config')) ) { + throw new Exception("Config class must extend Web2All_Manager_Config",E_USER_ERROR); + } + $this->Config=$config; + + /* + * start errorObserverable + * global accessable + */ + $this->PluginGlobal->Web2All_Manager_ErrorObserverable(); + + /* + * start default error catcher + */ + if (!Web2All_Manager_Error::getInstance()) { + Web2All_Manager_Error::newInstance($this); + } + + register_shutdown_function(array($this,"onRequestTerminate")); + + /* + * set the default config + */ + // add_includepath_to_path: define if the include path (relative ../../ from this location) is added to the php include path. Usefull for PEAR modules. + $this->DefaultConfig['add_includepath_to_path']=false; + // default no debugging, unless passed as constructor param + $this->DefaultConfig['debuglevel']=$debuglevel; + // when outputting debuglogs use the following method (echo|echoplain|error_log), default echo + $this->DefaultConfig['debugmethod']='echo'; + // when outputting debug logs, do we add a timestamp in front of each log entry? + $this->DefaultConfig['debug_add_timestamp']=false; + // when debugmethod==error_log, should we split the message on newlines? (multiline log) + $this->DefaultConfig['error_log_multiline']=true; + // when debugmethod==error_log, should we wrap too long lines? + $this->DefaultConfig['error_log_wrap']=true; + // Set error_reporting_level (int) to a error_reporting integer (or use predefined constants) + // defaults to E_ALL, but this can mean different things in different PHP versions, + // so you can use this to force consistent behaviour. + // you cannot binary operators in the config, so you will have to specify an integer if you + // want to combine constants. + $this->DefaultConfig['error_reporting_level']=E_ALL; + // default do not allow notice/warning/error messages to be suppressed (@ operator) + // when you set this to true, your default error_reporting level must be non-zero. + // because if it is zero, ALL errors will be suppressed, even if no @ is used + $this->DefaultConfig['allow_error_suppression']=false; + // enable or disable assertion + $this->DefaultConfig['assertionsenabled']=false; + // possibly override the assertion callback method (not reccommended) + $this->DefaultConfig['assertion_callback_method']=array('Web2All_Manager_Main','assertHandler'); + // Do not get ip from xforwarded, use REMOTE_ADDR + $this->DefaultConfig['get_ip_from_xforwarded']=false; + // Only useful if get_ip_from_xforwarded is true: it defines the amount of trusted proxies + $this->DefaultConfig['trusted_proxy_amount']=1; + + $this->ManagerConfig=$this->Config->makeConfig('Web2All_Manager_Main',$this->DefaultConfig); + + /* + * set error handlers + */ + $this->setErrorHandlers(); + + /* + * set the debug method + */ + $this->DebugMethod=$this->ManagerConfig['debugmethod']; + + /* + * do we add timestamp? + */ + $this->DebugAddTime=$this->ManagerConfig['debug_add_timestamp']; + + /* + * set the debug level + */ + $this->DebugLevel= $this->ManagerConfig['debuglevel']; + + /* + * set the debug multiline + */ + $this->DebugMultiLine=$this->ManagerConfig['error_log_multiline']; + + // assign the debuglevel to the error classes. + // but override debuglevel to zero if the debugmethod isn't echo or echoplain (because they only support echo method) + Web2All_Manager_ErrorHandler::$DebugLevel=(($this->DebugMethod=='echo' || $this->DebugMethod=='echoplain') ? $this->DebugLevel : 0); + Web2All_Manager_ExceptionHandler::$DebugLevel=(($this->DebugMethod=='echo' || $this->DebugMethod=='echoplain') ? $this->DebugLevel : 0); + Web2All_Manager_ErrorHandler::$allowErrorSuppression=$this->ManagerConfig['allow_error_suppression']; + + /* + * if needed add the include path to the php include path + */ + if(!defined('WEB2ALL_MANAGER_MAIN_INCLUDEPATH_APPENDED') && $this->ManagerConfig['add_includepath_to_path']){ + define('WEB2ALL_MANAGER_MAIN_INCLUDEPATH_APPENDED', true); + $include_path=dirname(__FILE__).'/../../'; + set_include_path($include_path . PATH_SEPARATOR . get_include_path() ); + } + + // configure assertion handling + assert_options(ASSERT_ACTIVE, $this->ManagerConfig['assertionsenabled']); + assert_options(ASSERT_BAIL, false); + assert_options(ASSERT_WARNING, false); + assert_options(ASSERT_CALLBACK, $this->ManagerConfig['assertion_callback_method']); + } + + /** + * Gets executed when request terminates + * + * WARNING: do not call exit() or errorhandling will break + */ + public function onRequestTerminate() + { + // lets see if there were unhandled fatal errors + $lasterror=error_get_last(); + if(!is_null($lasterror)){ + if($lasterror['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR)){ + // fatal error occured + // increase memory limit so we we can at least complete our error handling (in case it was Allowed memory size exhausted) + // (adding 1MB, unless already unlimited) + $memlim=Web2All_PHP_INI::getBytes(ini_get('memory_limit')); + if($memlim!=-1){ + ini_set('memory_limit',$memlim+1000000); + } + // then add this error to the observers + $error = Web2All_Manager_Error::getInstance(); + $exception = new Web2All_Manager_TriggerException($lasterror['type'], $lasterror['message'], $lasterror['file'], $lasterror['line'], array()); + if ($error) { + $error->setState($exception,$lasterror['type']); + } + } + } + } + + /** + * Set custom error handlers + */ + public function setErrorHandlers() + { + set_error_handler(array("Web2All_Manager_ErrorHandler", "errorHandlerCallback"), $this->ManagerConfig['error_reporting_level']); + set_exception_handler(array("Web2All_Manager_ExceptionHandler", 'errorHandlerCallback')); + } + + /** + * Restore original error handlers + */ + public function restoreErrorHandlers() + { + restore_error_handler(); + restore_exception_handler(); + } + + /** + * flush e-mail errors if any + * + * If there are any errors queued for emailing they will be e-mailed and the + * errorstate will be reset. This is useful for long running processes like daemons. + */ + public function flushEmailErrors() { + $this->PluginGlobal->Web2All_Manager_ErrorObserverable()->flushEmailErrors(); + } + + /** + * Suppress trace information when error occurs + * + */ + public function suppressTrace() + { + $errhnd=Web2All_Manager_Error::getInstance(); + $errhnd->suppressTrace(); + } + + /** + * Enable trace information when error occurs (default) + * + */ + public function enableTrace() + { + $errhnd=Web2All_Manager_Error::getInstance(); + $errhnd->enableTrace(); + } + + + /** + * Log the debug message to STDOUT (html) or STDERR + * Automaticly adds BR and newline when outputting to STDOUT. + * + * depends on config key 'debugmethod' which can be 'echo' or 'echoplain' or 'error_log' + * + * @param string $message + */ + public function debugLog($message) + { + if($this->DebugAddTime){ + // adding datetime to message (useful when DebugMethod=='echoplain') + if($this->DebugMultiLine){ + // multiline is enabled, so add the time for each line in the $message + $message_changed=''; + foreach(explode("\n",$message) as $sub_msg){ + if($message_changed!==''){ + // only add a newline for each newline we lost due to the explode + $message_changed.="\n"; + } + $message_changed.=date("Y-m-d H:i:s ").$sub_msg; + } + $message=$message_changed; + }else{ + $message=date("Y-m-d H:i:s ").$message; + } + } + if($this->DebugMethod=='error_log'){ + // error_log entries are automatically truncated at ~8130 chars. + // by default the message is displayed as one single line where newlines are replaced by literal \n + // in fact, all non ascii characters are replaced by escaped values (often \xnn) + // we support two config settings to change this behaviour: error_log_multiline, error_log_wrap + // both are enabled by default + if($this->DebugMultiLine){ + foreach(explode("\n",$message) as $sub_msg){ + if($this->ManagerConfig['error_log_wrap']){ + self::error_log_wrap($sub_msg); + }else{ + error_log($sub_msg); + } + } + }else{ + if($this->ManagerConfig['error_log_wrap']){ + self::error_log_wrap($message); + }else{ + error_log($message); + } + } + } else if($this->DebugMethod=='echoplain'){ + echo $message."\n"; + }else{ + echo $message."
\n"; + } + } + + /** + * Log the message to the error_log, but wrap at 8000 bytes + * to prevent automatic cutoff by the PHP error_log function + * + * @param string $message + */ + public static function error_log_wrap($message) + { + // msglen is the remain length of the message that needs to be written. + $msglen=strlen($message); + $i=0; + while($msglen>0){ + if($msglen>8000){ + // the remaining message that needs to be written is still longer than 8000 bytes + // so error_log only a chunk (start at offset: number of itereations * 8000) + error_log(substr($message,$i*8000,8000)); + $msglen=$msglen-8000; + }else{ + // less than 8000 bytes left, write the remainder (end) + error_log(substr($message,$i*8000)); + $msglen=0; + } + $i++; + } + } + + /** + * Returns true if debug method accepts html, or false if not + * + * @return boolean + */ + public function debugMethodAcceptsHtml() + { + if ($this->DebugMethod=='echo') + { + return true; + } + return false; + } + + /** + * Debug log the CPU used by the script + * + * Please note its only updated every 10ms. + * + * output: [u: ms / s:ms] + * + * @param string $message [optional message to prepend] + */ + public function debugLogCPU($message='') + { + // get resource usage + $dat = getrusage(); + $message.='[u:'.substr(($dat["ru_utime.tv_sec"]*1e6+$dat["ru_utime.tv_usec"])-$this->startUserCPU,0,-3).'ms / s:'.substr(($dat["ru_stime.tv_sec"]*1e6+$dat["ru_stime.tv_usec"])-$this->startSystemCPU,0,-3).'ms]'; + $this->debugLog($message); + } + + /** + * Debug log the memory used by the script + * + * output is in bytes + * + * @param string $message [optional message to prepend] + */ + public function debugLogMemory($message='') + { + $this->debugLog($message.memory_get_usage()); + } + + /** + * Functie om een global pointer naar een class uit te vegen. Alleen de + * pointer wordt verwijderd! De class blijft geinitialiseerd, en wordt niet + * gedestruct. + * + * @param string $classname + */ + final public function removeGlobalPointer($classname) { + $this->global_data->remove($classname); + } + + /** + * Retrieve from Global storage + * + * @param string $name + * @return mixed pointer to Plugin or false when not found + */ + public function getGlobalStorage($name) { + if (!$this->GlobalStorageExists($name)) { + return false; + } + return $this->global_data->get($name); + } + + /** + * Set Global storage + * + * @param string $name + * @param unknown_type $value + */ + public function setGlobalStorage($name,$value) { + $this->global_data->set($name,$value); + } + + /** + * Checks if $name exists in Global Storage + * + * @param string $name + * @return boolean + */ + final public function globalStorageExists($classname) { + if (!isset($this->global_data)) { + throw new Exception("Global storare is not initialised, can't check if object ".$classname." exists, forget to load parent::__construct?"); + } + return $this->global_data->offsetExists($classname); + } + + /** + * Load the classfile for the given class + * + * This method will blindly include the first php file which it finds + * for the given classname. It will not throw exceptions and won't indicate if the + * operation succeeded. It is used by both the autoloader and the loadClass() method, + * which is historically the method used to load a class. + * + * @param string $classname + * @param string $loadscheme [optional (Web2All|PEAR|INC|PLAIN) defaults to Web2All] + * @param string $package [optional packagename] + * @param boolean $set_includedir [optional bool, set true to add the package dir to include path] + */ + public static function includeClass($classname, $loadscheme='Web2All', $package='', $set_includedir=false) { + // $path will be the relative path to the classfile by exploding the namespace + $path = ''; + $filename = $classname; + // support namespaces + if(strpos($classname,'\\')){ + // ok, contains namespace + $path_parts = explode('\\',$classname); + $part_count=count($path_parts); + $filename = $path_parts[$part_count-1]; + $classname_without_namespaces = $filename; + for ($i=0;($i<$part_count-1);$i++) { + $path.=$path_parts[$i].DIRECTORY_SEPARATOR; + } + }else{ + $classname_without_namespaces = $classname; + } + + if ($loadscheme!='PLAIN') + { + $path_parts = explode("_",$classname_without_namespaces); + $part_count=count($path_parts); + $filename = $path_parts[$part_count-1]; + for ($i=0;($i<$part_count-1);$i++) { + $path.=$path_parts[$i].DIRECTORY_SEPARATOR; + } + } + + // depending on the scheme, select the suffix + // PEAR class files do not have the .class in the name. + // The Web2All scheme historically used ".class.php", but for + // better compatibility we now also support the plain ".php". + $classfilesuffixes=array('.class.php','.php'); + switch($loadscheme){ + case 'PEAR': + case 'PLAIN': + $classfilesuffixes=array('.php'); + break; + case 'INC': + $classfilesuffixes=array('.inc.php'); + break; + } + + foreach(self::$frameworkRoots as $include_path){ + if ($package) { + $include_path.=$package.DIRECTORY_SEPARATOR; + } + foreach($classfilesuffixes as $classfilesuffix){ + if(is_readable($include_path.$path.$filename.$classfilesuffix)){ + // if set_includedir is true, then we have the include path of the package to + // the PHP environment include path + if ($set_includedir) { + $pathArray = explode( PATH_SEPARATOR, get_include_path() ); + // only add the path if its not already in the include path + if (!in_array($include_path,$pathArray)) { + $pathArray[]=$include_path; + set_include_path(implode(PATH_SEPARATOR,$pathArray)); + } + } + include_once($include_path.$path.$filename.$classfilesuffix); + // ok once we found a file, we are done + return; + } + } + } + } + + /** + * Include php file for Web2All_Manager_Plugin + * + * @param string $classname + * @param string $loadscheme [optional (Web2All|PEAR|INC|PLAIN) defaults to Web2All] + * @param string $package [optional packagename] + * @param boolean $set_includedir [optional bool, set true to add the package dir to include path] + */ + public static function loadClass($classname, $loadscheme='Web2All', $package='', $set_includedir=false) { + $class_exists=class_exists($classname) || interface_exists($classname); + if ($class_exists && !$set_includedir) { + // if class already exists, we don't need to do a thing + // but one CAVEAT: if the class was loaded with $set_includedir==false and now the $set_includedir==true + // then we do not add the path to the include path. + return; + } + + self::includeClass($classname, $loadscheme, $package, $set_includedir); + } + + /** + * Initialize Web2All_Manager_Plugin + * returns Web2All_Manager_Plugin object + * + * @param string $classname + * @param array $arguments + * @param boolean $isplugin Force the first constructor param to be the + * Web2All_Manager_Main object. Set false for + * automatically detection if this is required. + * @return object + */ + public function initClass($classname,$arguments=array(),$isplugin=true) + { + // when we no longer have any PHP 5.2 we can replace below with static::loadClass($classname); + // we cannot use self::loadClass($classname); because it will break extending Main classes which + // redefine the loadClass method. (we need late static binding) + /* + * If class cannot be found, include corresponding file + */ + $this->loadClass($classname); + + /* + * If $classname still doesn't exists, the class cannot be started + */ + if (!class_exists($classname)) { + throw new Exception("Class whith name '$classname' does not exists. Cannot initialise $classname",E_USER_ERROR); + } + + /* + * Start class and return object + */ + $reflectionObj = new ReflectionClass($classname); + if($reflectionObj->isSubclassOf('Web2All_Manager_Plugin') || $isplugin){ + // directly extends Web2All_Manager_Plugin so it expects the Web2All_Manager_Main object + // as first constructor param. Or the $isplugin is set true so force first param to + // Web2All_Manager_Main object. + array_unshift($arguments,$this); + }elseif($reflectionObj->implementsInterface('Web2All_Manager_PluginInterface')){ + // class does not extend Web2All_Manager_Plugin, but the class still implements + // the Web2All_Manager_PluginInterface so we can call the setWeb2All method to + // init the object after construction. + $obj=call_user_func_array(array(&$reflectionObj, 'newInstance'), $arguments); + $obj->setWeb2All($this); + return $obj; + } + return call_user_func_array(array(&$reflectionObj, 'newInstance'), $arguments); + } + + /** + * This function handles failed assertions (callback method) + * + * @param string $script + * @param int $line + * @param string $message + */ + public static function assertHandler($script, $line, $message) + { + trigger_error('Assertion failed: in '."$script, on line $line, $message",E_USER_WARNING); + } + + /** + * Returns the IP address from which the user is viewing the current page. + * Defaults to $_SERVER['REMOTE_ADDR'], unless the get_ip_from_xforwarded + * config is set to true, then $ _SERVER["HTTP_X_FORWARDED_FOR"] is used + * if available. + * + * Use get_ip_from_xforwarded only in a situation where we can trust + * HTTP_X_FORWARDED_FOR. Like when we generate this field ourself on our own + * proxy server. + * + * For logging purposes its perhaps better to both log REMOTE_ADDR and HTTP_X_FORWARDED_FOR + * when don't use our own proxy server. + * + * X-Forwarded-For: http://en.wikipedia.org/wiki/X-Forwarded-For + * + * @return string + */ + public function getIP() { + + if ($this->ManagerConfig['get_ip_from_xforwarded'] && isset($_SERVER["HTTP_X_FORWARDED_FOR"]) && trim(strtolower($_SERVER["HTTP_X_FORWARDED_FOR"]))!='unknown' ) { + // When viewed through an anonymous proxy, the address string can contain multiple ip's separated by commas. + // http://www.jamescrowley.co.uk/2007/06/19/gotcha-http-x-forwarded-for-returns-multiple-ip-addresses/ + $ip_array = explode(",", $_SERVER["HTTP_X_FORWARDED_FOR"]); + // Use last ip addres, the last ip address is from the previous proxy, or from the user itself if there + // was no previous proxy, HTTP_X_FORWARDED_FOR can't be trusted, only the last entry added by our (first) + // trusted proxy server is the most trusted one we know. + // The trusted_proxy_amount config defines how many proxies we can trust, defaults to 1 + for($i=0;$i<$this->ManagerConfig['trusted_proxy_amount'];$i++){ + if(count($ip_array)==0){ + // no more ips in HTTP_X_FORWARDED_FOR + // this means there are less actual proxies than our trusted_proxy_amount setting. + // so people are bypassing our proxy of the trusted_proxy_amount setting is wrong. + break; + } + $ip = array_pop($ip_array); + } + return trim($ip); + } + if (!isset($_SERVER["REMOTE_ADDR"])) { + return ''; + } + return $_SERVER["REMOTE_ADDR"]; + } + + /** + * Instantiates Web2All_Manager_Main + * + * This shorthand instantiation has a few prequisites: + * - the include file /include/include.php must exist + * - this include file must have the WEB2ALL_CONFIG_CLASS constant defined + * - the WEB2ALL_CONFIG_CLASS constant must contain the classname of a config class which + * extends Web2All_Manager_Config + * + * Will generate E_COMPILE_ERROR if include.php does not exist + * Will throw Exception if classname is not defined or cannot be found + * + * This is a replacement for the auto_prepend_file mechanism we used in the + * .htaccess file. + * + * Usage: + * $web2all=Web2All_Manager_Main::newInstance(); + * + * @param string $includefile [optional, defaults to include.php] include file relative to + * the include directory. + * @return Web2All_Manager_Main + */ + public static function newInstance($includefile='include.php') + { + // throws compile error if not found + require_once(dirname(__FILE__).'/../../'.$includefile); + + if(!defined('WEB2ALL_CONFIG_CLASS')){ + throw new Exception('WEB2ALL_CONFIG_CLASS not defined!'); + } + + // load config class + Web2All_Manager_Main::loadClass(WEB2ALL_CONFIG_CLASS); + + // instantiate config class + $classname=WEB2ALL_CONFIG_CLASS; + $config = new $classname(); + + // instantiate Web2All_Manager_Main + return new Web2All_Manager_Main($config); + } + + /** + * Registers an include root directory + * + * Used by autoloader and loadClass() + * + * @param string $root directory path + * @return boolean was the directory added + */ + public static function registerIncludeRoot($root=null) + { + if(is_null($root)){ + $root = dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR; + } + if(!in_array($root, self::$frameworkRoots)){ + self::$frameworkRoots[] = $root; + return true; + } + return false; + } + + /** + * Unregisters an include root directory + * + * @param string $root directory path + * @return boolean was the directory removed + */ + public static function unregisterIncludeRoot($root) + { + $found_key=array_search($root, self::$frameworkRoots, true); + if($found_key!==false){ + unset(self::$frameworkRoots[$found_key]); + return true; + } + return false; + } + + /** + * Get all registered include root directories + * + * @return array + */ + public static function getRegisteredIncludeRoots() + { + return self::$frameworkRoots; + } + + /** + * Registers an autoloader for the Web2All framework + * + * it will call the Web2All_Manager_Main::loadClass + * + * @return boolean + */ + public static function registerAutoloader($root=null) + { + self::registerIncludeRoot($root); + if(!self::$autoloaderAdded){ + self::$autoloaderAdded = true; + return spl_autoload_register(array('Web2All_Manager_Main','loadClass')); + } + return true; + } + +} + +/** + * @name Web2All_Manager_ErrorObserverable class + * + * Warning: this class could use some refactoring, it implements the Observer Design Pattern, + * but not very logically. For some reason the interface methods are not used. Also it actually + * contains two separate lists of observers: + * - observer_names : list of classnames, the observers are stored in PluginGlobal storage and + * instantiated on demand. Three observers are added by default here. + * - observers : list of observer objects. Not actually used unless someone adds custom + * observers with addObserver() or attach() method. As far as I know this + * was never actually done. + * + * @author Hans Oostendorp + * @copyright (c) Copyright 2007-2014 by Web2All + * @version 0.2 + * @since 2007-05-29 + **/ +class Web2All_Manager_ErrorObserverable extends Web2All_Manager_Plugin implements SplSubject { + + /** + * list of uninilialised observer objects by name + * + * @var array + */ + protected $observer_names = array(); + + /** + * list of initialised observer objects + * + * @var array + */ + protected $observers = array(); + + /** + * Store for occured Exception/Error + * + * @var Web2All_Manager_ErrorData + */ + protected $errordata; + + /** + * Constructor, add Observers, depending on enviroment + */ + public function __construct(Web2All_Manager_Main $Web2All) { + if($Web2All->DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + echo "[Start de errorObserverable Class]
\n"; + } + parent::__construct($Web2All); + /** + * attach default errorObservers + */ + $this->addObserver_name('Web2All_ErrorObserver_ErrorLog'); + $this->addObserver_name('Web2All_ErrorObserver_Email'); + $this->addObserver_name('Web2All_ErrorObserver_Display'); + } + + /** + * Adds an observer to the set of observers for this object, provided that it is not the same as some observer already in the set. + * + * @param $observer SplObserver, Object of an observer to be added. + */ + public function addObserver(SplObserver $observer) { + if(!$this->containObserver($observer)) { + $this->observers[] = $observer; + } + } + + /** + * Adds an observer to the set of observers for this object, provided that it is not the same as some observer already in the set. + * + * @param $classname String, classname of an observer to be added. + */ + public function addObserver_name($classname) { + if(!$this->containObserver_name($classname)) { + $this->observer_names[] = $classname; + } + } + + /** + * Deletes an observer from the set of observers of this object. + * + * @param $observer SplObserver, Object of the observer to be deleted. + */ + public function deleteObserver(SplObserver $observer) { + $this->observers = array_diff($this->observers, array($observer)); + } + + /** + * Deletes an observer from the set of observers of this object. + * + * @param $classname String, classname of the observer to be deleted. + */ + public function deleteObserver_name($classname) { + $this->observer_names = array_diff($this->observer_names, array($classname)); + } + + /** + * Clears the observer list so that this object no longer has any observers. + */ + public function deleteObservers() { + unset($this->observers); + $this->observers = array(); + unset($this->observer_names); + $this->observer_names = array(); + } + + /** + * If this object has changed, as indicated by the hasChanged method, then + * start observers as needed and notify them, and then call the clearChanged + * method to indicate that this object has no longer changed. + */ + public function notifyObservers() { + /** + * restore original error handler, to avoid problems when errors occurres + * in handling other errors + */ + $this->Web2All->restoreErrorHandlers(); + foreach($this->observers as $observer) { + $observer->update($this); + } + foreach($this->observer_names as $classname) { + $this->Web2All->PluginGlobal->$classname->update($this); + } + // set custom error handlers back + $this->Web2All->setErrorHandlers(); + } + + /** + * flush e-mail errors if any + * + * If there are any errors queued for emailing they will be e-mailed and the + * errorstate will be reset. This is useful for long running processes like daemons. + */ + public function flushEmailErrors() { + foreach($this->observers as $observer) { + if(get_class($observer)=='Web2All_ErrorObserver_Email'){ + $observer->flushErrors(); + } + } + foreach($this->observer_names as $classname) { + if($classname=='Web2All_ErrorObserver_Email'){ + $this->Web2All->PluginGlobal->$classname->flushErrors(); + } + } + } + + /** + * Returns the number of observers of this Observable object. + * + * @return integer the number of observers of this object. + */ + public function countObservers() { + return count($this->observers)+count($this->observer_names); + } + + /** + * Check if observer already in the list + * + * @param $observer SplObserver + * @return bool + */ + public function containObserver(SplObserver $observer) { + return in_array($observer, $this->observers); + } + + /** + * Check if observer already in the list + * + * @param $classname + * @return bool + */ + public function containObserver_name($classname) { + return in_array($classname, $this->observer_names); + } + + /** + * Add observer + * + * @param $observer SplObserver + */ + public function attach(SplObserver $observer) { + $this->addObserver($observer); + } + + /** + * Add observer + * + * @param $classname + */ + public function attach_name($classname) { + $this->addObserver_name($classname); + } + + /** + * Remove observer + * + * @param SplObserver $observer + */ + public function detach(SplObserver $observer) { + $this->deleteObserver($observer); + } + + /** + * Remove observer + * + * @param String $classname + */ + public function detach_name($classname) { + $this->deleteObserver_name($classname); + } + + /** + * Notify observers + */ + public function notify() { + $this->notifyObservers(); + } + + /** + * @return Web2All_Manager_ErrorData + */ + public function getState() { + return $this->errordata; + } + + /** + * @param Web2All_Manager_ErrorData + */ + public function setState($errordata) { + $this->errordata = $errordata; + $this->notify(); + } +} + +/** + * Custom Exception, used by errorhandler when encountering a "trigger_error" + * + * @package Web2All_Manager + * @name Web2All_Manager_TriggerException + * @author Merijn van den Kroonenberg + */ +class Web2All_Manager_TriggerException extends Exception { + public $context; + + public $triggertrace; + + public function __construct($code, $string, $file, $line, $context, $triggertrace=array()) { + parent::__construct($string, $code); + + if ($this->getCode()==0) { + $this->code = E_USER_ERROR; + } + $this->line = $line; + $this->file = $file; + $this->context = $context; + // store own trace, filled by debug_backtrace(), if param is available + // getTrace() contains invalid traces when invoked by trigger_error, so we store our own trace + // first entry contains calling errorHandlerCallback, is not needed, pop it off + if (count($triggertrace)>0) + { + unset($triggertrace[0]); + $this->triggertrace = $triggertrace; + } + } + +} + +/** + * @name Web2All_Manager_ErrorHandler class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007 by Web2All + * @version 0.2 + * @since 2007-05-29 + * + * Catch all standard PHP error's and throw exception instead, when needed + */ +class Web2All_Manager_ErrorHandler { + public static $DebugLevel=0; + public static $allowErrorSuppression=false; + + public static function errorHandlerCallback($code, $string, $file, $line, $context) { + if(self::$DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + echo "[Start de Web2All_Manager_ErrorHandler->errorHandlerCallback]
\n"; + } + + // check if error is suppressed + // please note this only works if the error_reporting level is not 0 by default + if ( self::$allowErrorSuppression && error_reporting() == 0 ) { + if(self::$DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + echo "Web2All_Manager_ErrorHandler->errorHandlerCallback: error was suppressed
\n"; + } + return; + } + + $exception = new Web2All_Manager_TriggerException($code, $string, $file, $line, $context, debug_backtrace()); + + /* + * Web2All_Manager_ExceptionHandler cannot extend + * Web2All_Manager_Main class, and needs a seperate class for + * triggering error + */ + $error = Web2All_Manager_Error::getInstance(); + if (!$error) { + die("Error class not started yet, cannot handle error!"); + } + $error->setState($exception,$exception->getCode()); + + // now, for some error codes, we have to halt execution (most below cannot be catched anyway) + if($code & (E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR)){ + exit(1); + } + } +} + +/** + * @name Web2All_Manager_ExceptionHandler class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007 by Web2All + * @version 0.2 + * @since 2007-05-29 + * + * Used to catch all PHP exceptions and trigger Web2All error handling + */ +class Web2All_Manager_ExceptionHandler { + public static $DebugLevel=0; + + public static function errorHandlerCallback($exception) { + if(self::$DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + echo "[Start de Web2All_Manager_ExceptionHandler->errorHandlerCallback]
\n"; + } + /* + * Web2All_Manager_ExceptionHandler cannot extend Web2All class, and + * needs a seperate class for triggering error + */ + $error = Web2All_Manager_Error::getInstance(); + if (!$error) { + die("Error class not started yet, cannot handle error!"); + } + $error->setState($exception,E_USER_ERROR); + // PHP will exit after this callback, but will do so with status 0 + // as it is an unhandled exception we want a non-zero exit status + exit(1); + } +} + +/** + * @name Web2All_Manager_ErrorData class + * @author Merijn van den Kroonenberg + * @copyright (c) Copyright 2007 Web2All B.V. + * @version 0.1 + * @since 2007-07-06 + * + * Used to store error information for the errorobservables + */ +class Web2All_Manager_ErrorData { + public $severity; + public $exception; + public $suppresstrace; + + public function __construct($exception, $severity, $suppresstrace=false) { + $this->setSeverity($severity); + $this->setException($exception); + $this->suppresstrace=$suppresstrace; + } + + public function getSeverity() { + return $this->severity; + } + + public function setSeverity($severity) { + $this->severity=$severity; + } + + public function getException() { + return $this->exception; + } + + public function setException($exception) { + $this->exception=$exception; + } + + /** + * check if error trace information must be suppressed + * + * returns true if it must be suppressed, false otherwise. + * + * @return boolean + */ + public function getTraceSuppression() + { + return $this->suppresstrace; + } + +} + +/** + * @name Web2All_Manager_Registry class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007 by Web2All + * @version 0.1 + * @since 2007-05-16 + */ +class Web2All_Manager_Registry Implements ArrayAccess, Iterator, Countable { + const VERSION = 0.1; + static $instance = false; + private $vars = array(); + public static $DebugLevel=0; + + /** + * Constructor, can only be triggerd one time, by + * Web2All_Manager_Registry class only + */ + private function __construct() { + if(self::$DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + echo "[Start de Web2All_Manager_Registry Class]
\n"; + } + $this->vars = array(); + } + + /** + * factory method to return the singleton instance + * + * @return Web2All_Manager_Registry object + */ + public static function getInstance() { + if(self::$DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + echo "[Vraag een instantie van de Web2All_Manager_Registry Class op]
\n"; + } + if (!Web2All_Manager_Registry::$instance) { + Web2All_Manager_Registry::$instance = new Web2All_Manager_Registry; + } + return Web2All_Manager_Registry::$instance; + } + + /** + * Triggered when Web2All_Manager_Registry class is accessed directly + * Get value for registry $key, returns false if not found + * + * @param string $key + * @return unknown + */ + public function __get($key) { + return $this->offsetGet($key); + } + + /** + * Triggered when Web2All_Manager_Registry class is accessed directly + * Set registry variable $key whith value $var + * Returns if succeeded or not + * + * @param string $key + * @param unknown_type $value + * @return boolean + */ + public function __set($key,$value) { + return $this->offsetSet($key,$value); + } + + /** + * Set registry variable $key whith value $var + * Returns if succeeded or not + * + * @param string $key + * @param unknown_type $value + * @return boolean + */ + public function set($key,$value) { + return $this->offsetSet($key,$value); + } + + /** + * Get value for registry $key, returns false if not found + * + * @param string $key + * @return unknown + */ + public function get($key) { + return $this->offsetGet($key); + } + + /** + * Remove variable $key from registry, returns false if not found + * + * @param unknown_type $var + * @return boolean + */ + public function remove($key) { + return $this->offsetUnset($key); + } + + /** + * Echo's content of Web2All_Manager_Registry + * + * @return unknown + */ + public function show_reg() { +?> +
vars); ?>
+vars[$offset]); + } + + /** + * -- ArrayAccess + * Get value for registry $key, returns false if not found + * + * @param string $offset + * @return unknown + */ + public function offsetGet($offset) { + if (!$this->offsetExists($offset)) { + throw new RangeException("Var $offset doesn't exists in Web2All_Manager_Registry"); + return false; + } + return $this->vars[$offset]; + } + + /** + * -- ArrayAccess + * Set registry variable $offset whith value $var + * Returns if succeeded or not + * + * @param string $offset + * @param unknown_type $value + * @return boolean + */ + public function offsetSet($offset,$value) { + if ($this->offsetExists($offset)) { + throw new RangeException("Var $offset allready exists in Web2All_Manager_Registry"); + return false; + } + $this->vars[$offset]=&$value; + return true; + } + + /** + * Remove variable $key from registry, returns false if not found + * + * @param unknown_type $var + * @return boolean + */ + public function offsetUnset($offset) { + if (!$this->offsetExists($offset)) { + throw new RangeException("Var $offset doesn't exists in Web2All_Manager_Registry"); + return false; + } + unset($this->vars[$offset]); + return true; + } + + /** + * -- Iterator + * Get value for current registry position + * + * @return unknown + */ + public function current() { + return current($this->vars); + } + + /** + * -- Iterator + * Advance the internal array pointer of $vars + * Returns the array value in the next place that's pointed to by the internal + * array pointer, or FALSE if there are no more elements. + * + * @return mixed + */ + public function next() { + return next($this->vars); + } + + /** + * -- Iterator + * Returns the index element of the current array position + * + * @return mixed + */ + public function key() { + return key($this->vars); + } + + /** + * -- Iterator + * Sets the internal array pointer of $vars to the beginning of array + * Returns TRUE on success or FALSE on failure. + * + * @return boolean + */ + public function rewind() { + reset($this->vars); + } + + /** + * -- Iterator + * Checks if there is a current element in the array + * Returns TRUE on success or FALSE on failure. + * + * @return boolean + */ + public function valid() { + return ($this->current()!==false); + } + + /** + * -- Countable + * Count elements in array + * + * @return int + */ + public function count() { + return count($this->vars); + } + + /** + * Function to stop cloning of Web2All_Manager_Registry class + */ + public function __clone() { + throw new Exception('Clone of registry class is not allowed.', E_USER_ERROR); + } +} + +/** + * Web2All_Manager_Config + * + * This is the baseclass for all plugin configs. + * Every project can extend this class. The extended class can + * specify a protected property for every plugin which needs a config entry. + * + */ +class Web2All_Manager_Config { + + /** + * constructor + * + * allow calling of parent constructor in extending classes + */ + public function __construct() { + + } + + /** + * prevent setting of properties + * + */ + public final function __set($member, $value) { + throw new Exception('You cannot set a constant. ('.$member.')',E_USER_ERROR); + } + + /** + * Allow getting of protected properties + */ + public final function __get($member) { + if (!isset($this->$member)) { + return NULL; + } + return $this->$member; + } + + /** + * Build a config array for a specific plugin, if config settings are not set, + * use the default config value. All keys in $overrulingconfig will always + * override the values in this config and the defaultconfig. + * + * @param string $pluginname + * @param array $defaultpluginconfig + * @param array $overrulingconfig [optional] all key/values will be leading + * @return array + */ + public function makeConfig($pluginname,$defaultpluginconfig,$overrulingconfig=null) + { + $pluginconfig=array(); + + $customconfig=array(); + if (isset($this->$pluginname) && is_array($this->$pluginname)) { + $customconfig=$this->$pluginname; + } + $pluginconfig=array_merge($defaultpluginconfig,$customconfig); + + // override config values + if (isset($overrulingconfig) && is_array($overrulingconfig)) { + $pluginconfig=array_merge($pluginconfig,$overrulingconfig); + } + + return $pluginconfig; + } + + /** + * Validate a specific plugin config against the given array + * with config keys. The pluginconfig must exist and each configkey + * in requiredconfig must exist also. When not valid, an exception will be thrown + * (can be catched in calling method) + * + * @param string $pluginname + * @param array $requiredconfig + * @param array $overrulingconfig [optional] overruled config values do not need to be present in config + * @return boolean (always true) throws Exception on error + */ + function validateConfig($pluginname,$requiredconfig=array(),$overrulingconfig=null) + { + // first check if the pluginconfig is available at all + if (!(isset($this->$pluginname) && is_array($this->$pluginname))) { + throw new Exception('No config defined for plugin '.$pluginname); + } + // then for each key in $requiredconfig check if it is available in this config and + // validate it + // *TODO* implement different validation types. + foreach ($requiredconfig as $configkey => $validation_type) { + if (!array_key_exists($configkey,$this->$pluginname) && !(is_array($overrulingconfig) && array_key_exists($configkey,$overrulingconfig))) { + // config key doesn't exist, raise error + throw new Exception('Required config key '.$configkey.' for plugin '.$pluginname.' is not defined'); + } + + } + return true; + } + + +} + +/** + * Web2All error handling. + * This class must be initialised in the beginning of the script, usally by the + * Web2All_Manager_Main class. The errorHandler and exceptionHandler + * (who are triggered by PHP of the user), trigger this class. This error class + * has access to the Web2All_Manager_Main class and thus the + * ErrorOverServerable. + * All we have todo is change the state of the error Observerable + * + * @name Web2All_Manager_Error class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007 by Web2All + * @version 0.1 + * @since 2007-05-29 + */ +class Web2All_Manager_Error extends Web2All_Manager_Plugin { + const VERSION = 0.1; + static $instance = false; + + protected $suppresstrace=false; + + /** + * Override the web2all property so it becomes public and + * can be accessed from the static methods. + * + * @var Web2All_Manager_Main + */ + public $Web2All; + + /** + * Constructor + */ + public function __construct(Web2All_Manager_Main $Web2All) { + if($Web2All->DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + $Web2All->debugLog( "[Start de Web2All_Manager_Error Class]"); + } + parent::__construct($Web2All); + } + + /** + * factory method to return the singleton instance + * + * @return Web2All_Manager_Error object + */ + public static function getInstance() { + if(self::$instance && self::$instance->Web2All->DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + // we can only log if $instance is set, because this a static method and + // we don't have a reference to the web2all class. + self::$instance->Web2All->debugLog( "[Vraag een instantie van de Web2All_Manager_Error Class op]"); + } + return self::$instance; + } + + public static function newInstance(Web2All_Manager_Main $Web2All) { + if($Web2All->DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + $Web2All->debugLog( "[Set een instantie van de Web2All_Manager_Error Class]"); + } + if (Web2All_Manager_Error::getInstance()) { + die("Can't start new instance of Web2All_Manager_Error class twice"); + } + Web2All_Manager_Error::$instance = new Web2All_Manager_Error($Web2All); + return Web2All_Manager_Error::$instance; + } + + public function setState($exception,$severity) { + if($this->Web2All->DebugLevel >= Web2All_Manager_Main::DEBUGLEVEL_FULL) { + $this->Web2All->debugLog( "Web2All_Manager_Error->setState"); + } + $errobj=$this->Web2All->Factory->Web2All_Manager_ErrorData($exception,$severity,$this->suppresstrace); + $this->Web2All->PluginGlobal->Web2All_Manager_ErrorObserverable->setState($errobj); + } + + public function suppressTrace() + { + $this->suppresstrace=true; + } + + public function enableTrace() + { + $this->suppresstrace=false; + } + + + /** + * Returns version number of this class + * + * @return Double, version number + */ + public function getVersion() { + return (double)self::VERSION; + } +} + +/** + * Global Web2All_Manager_Plugin Loader + * Auto include and initialise Web2All_Manager_Plugins for global use + * DOES NOT SUPPORT classes with constructor params (they get lost) + * + * @name Web2All_Manager_PluginLoaderGlobal class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007-2009 by Web2All + * @version 0.1 + * @since 2007-06-14 + */ +class Web2All_Manager_PluginLoaderGlobal extends Web2All_Manager_Plugin { + + /** + * Deze functie wordt automatisch aangeroepen als direct een method van een + * class wordt aangeroepen. + * (bijvoorbeeld $Web2All->Web2All_Manager_PluginLoaderPrivate->test->print(); + * Deze functie krijgt dan test mee als parameter. + */ + public function __get($classname) { + if (!$this->Web2All->globalStorageExists($classname)) { + $class = $this->Web2All->initClass($classname); + $this->Web2All->setGlobalStorage($classname,$class); + } + return $this->Web2All->getGlobalStorage($classname); + } + + /** + * Deze functie wordt automatisch aangeroepen als direct een class + * wordt aangeroepen zonder method. Bijvoorbeeld $Web2All->test(4); + * Dit betekend dat een request wordt gedaan om die class te initialiseren. + */ + public function __call($classname,$arguments) { + // this could be adjusted to serialize and hash the arguments and use them + // as storage key with the classname. right now the arguments are (silently) ignored. + // and i consider this bad behaviour. :merijn + if ($this->Web2All->globalStorageExists($classname)) { + if (count($arguments)>0) { + throw new exception("Class with name '$classname' allready exists in global storage of Web2All_Manager_Main class, can't initialize with new constructor params"); + } + return $this->Web2All->getGlobalStorage($classname); + } + $class = $this->Web2All->initClass($classname); + $this->Web2All->setGlobalStorage($classname,$class); + return $this->Web2All->getGlobalStorage($classname); + } + +} + +/** + * Private Web2All_Manager_Plugin Loader + * Auto include en initialise Web2All_Manager_Plugins for private use + * + * @name Web2All_Manager_PluginLoaderPrivate class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007 by Web2All + * @version 0.1 + * @since 2007-06-14 + */ +class Web2All_Manager_PluginLoaderPrivate extends Web2All_Manager_Plugin { + + /** + * Deze functie wordt automatisch aangeroepen als direct een method van een + * class wordt aangeroepen. + * (bijvoorbeeld $Web2All->Web2All_Manager_PluginLoaderPrivate->test->print(); + * Deze functie krijgt dan test mee als parameter. Het gaat altijd om een + * nieuwe class die geinitialiseerd moet worden, aangezien de pointers van + * classen bij private geinitialiseerde plugins niet opgeslagen worden. De + * client side moet dat zelf regelen. De __get wordt eigenlijk alleen + * getriggerd als eenmalig een methos van een class aangeroepen wordt en de + * class daarna niet meer nodig is. + */ + public function __get($classname) { + return $this->Web2All->initClass($classname); + } + + /** + * Deze functie wordt automatisch aangeroepen als direct een class + * wordt aangeroepen zonder method. Bijvoorbeeld $Web2All->test(4); + * Dit betekend dat een request wordt gedaan om die class te initialiseren. + * Bij private aangeroepen Web2All_Manager_Plugins zal voornamelijk __call getriggerd worden + * en niet __get. + */ + public function __call($classname,$arguments) { + return $this->Web2All->initClass($classname,$arguments); + } + +} + +/** + * Private Web2All_Manager_Factory Loader + * Auto include en initialise non plugins for private use + * + * @name Web2All_Manager_Factory class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007 by Web2All + * @version 0.1 + * @since 2007-06-15 + */ +class Web2All_Manager_Factory extends Web2All_Manager_Plugin { + + /** + * Deze functie wordt automatisch aangeroepen als direct een method van een + * class wordt aangeroepen. + * (bijvoorbeeld $Web2All->Web2All_Manager_Factory->test->print(); + * Deze functie krijgt dan test mee als parameter. Het gaat altijd om een + * nieuwe class die geinitialiseerd moet worden, aangezien de pointers van + * classen bij private geinitialiseerde plugins niet opgeslagen worden. De + * client side moet dat zelf regelen. De __get wordt eigenlijk alleen + * getriggerd als eenmalig een method van een class aangeroepen wordt en de + * class daarna niet meer nodig is. + */ + public function __get($classname) { + return $this->Web2All->initClass($classname,array(),false); + } + + /** + * Deze functie wordt automatisch aangeroepen als direct een class + * wordt aangeroepen zonder method. Bijvoorbeeld $Web2All->test(4); + * Dit betekend dat een request wordt gedaan om die class te initialiseren. + * Bij private aangeroepen Web2All_Manager_Plugins zal voornamelijk __call + * getriggerd worden en niet __get. + */ + public function __call($classname,$arguments) { + return $this->Web2All->initClass($classname,$arguments,false); + } + +} + +/** + * Private Web2All_Manager_ClassInclude Loader + * Auto include non plugins for private use + * + * @name Web2All_Manager_ClassInclude class + * @author Hans Oostendorp + * @copyright (c) Copyright 2007 by Web2All + * @version 0.2 + * @since 2007-07-05 + */ +class Web2All_Manager_ClassInclude extends Web2All_Manager_Plugin { + + /** + * Include php file + * + * @param string $classname + * @param string $loadscheme [optional (Web2All|PEAR|INC|PLAIN) defaults to Web2All] + * @param string $package [optional packagename] + * @param boolean $set_includedir [optional bool, set true to add the package dir to include path] + */ + public static function loadClassname($classname,$loadscheme='Web2All', $package='', $set_includedir=false) { + Web2All_Manager_Main::loadClass($classname,$loadscheme,$package,$set_includedir); + } + +} + +/** + * Class for encapsualting a password string + * + * Encapsulated passwords are not shown in the error trace + * when an error occurs. + * + * Please not this class does not extend Web2All_Manager_Plugin, + * use the Factory to construct, instead of Plugin. + * + */ +class Web2All_Manager_EncapsulatedPassword { + + /** + * the password + * + * @var string + */ + public $password; + + /** + * Constructor + * @param string $password [optional password] + */ + public function __construct($password='') { + if (!is_string($password)) { + throw new InvalidArgumentException('Web2All_Manager_EncapsulatedPassword: constructor param must be a string'); + } + + $this->password=$password; + } + + /** + * return the password string + * + * @return string + */ + public function __toString() + { + return $this->password; + } + +} + +/** + * Class for handling PHP ini settings + * + * The method getBytes is used in the shutdown function of + * manager main, thats the reason this class is included here. + * + * Please not this class does not extend Web2All_Manager_Plugin, + * use the Factory to construct, instead of Plugin. + * + */ +class Web2All_PHP_INI { + + /** + * converts possible shorthand notations from the php ini + * to bytes. + * + * see: http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes + * + * original code from: http://nl2.php.net/manual/en/function.ini-get.php#96996 + * + * @return int + */ + public static function getBytes($size_str) + { + switch (substr ($size_str, -1)) + { + case 'M': case 'm': return (int)$size_str * 1048576; + case 'K': case 'k': return (int)$size_str * 1024; + case 'G': case 'g': return (int)$size_str * 1073741824; + default: return $size_str; + } + } + +} +?> \ No newline at end of file diff --git a/src/Web2All/Manager/PrsLogger.class.php b/src/Web2All/Manager/PrsLogger.class.php new file mode 100644 index 0000000..f7ba8e7 --- /dev/null +++ b/src/Web2All/Manager/PrsLogger.class.php @@ -0,0 +1,57 @@ +Web2All->debugLog('['.$level.'] '.$this->interpolate($message, $context)); + } + + /** + * Interpolates context values into the message placeholders. + * + * @param string $message + * @param array $context + * @return string + */ + function interpolate($message, array $context = array()) + { + // build a replacement array with braces around the context keys + $replace = array(); + foreach ($context as $key => $val) { + // check that the value can be casted to string + if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace['{' . $key . '}'] = $val; + } + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } + +} +?> \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderNoAutoLoadTest.php b/tests/Web2All/Manager/ClassLoaderNoAutoLoadTest.php new file mode 100644 index 0000000..1913fb7 --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderNoAutoLoadTest.php @@ -0,0 +1,70 @@ +assertTrue(class_exists('Web2All_ErrorObserver_Display', true)); + }else{ + $this->markTestSkipped('Cannot run this test because other tests made it impossible, run this test separate'); + } + } + + /** + * Test loadClass with .inc files + * + */ + public function testLoadclassInc() + { + Web2All_Manager_Main::unregisterIncludeRoot(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); + Web2All_Manager_Main::loadClass('Web2All_Manager_ClassLoaderTest_B','INC'); + $this->assertFalse(class_exists('Web2All_Manager_ClassLoaderTest_B', false)); + Web2All_Manager_Main::registerIncludeRoot(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); + Web2All_Manager_Main::loadClass('Web2All_Manager_ClassLoaderTest_B','INC'); + $this->assertTrue(class_exists('Web2All_Manager_ClassLoaderTest_B', false)); + } + + /** + * Test loadClass with namespaces + * + */ + public function testLoadclassNamespace() + { + Web2All_Manager_Main::unregisterIncludeRoot(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); + Web2All_Manager_Main::loadClass('Web2All\\Manager\\ClassLoaderTest\\E'); + $this->assertFalse(class_exists('Web2All\\Manager\\ClassLoaderTest\\E', false)); + Web2All_Manager_Main::registerIncludeRoot(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); + Web2All_Manager_Main::loadClass('Web2All\\Manager\\ClassLoaderTest\\E'); + $this->assertTrue(class_exists('Web2All\\Manager\\ClassLoaderTest\\E', false)); + } + + /** + * Test loadClass with mixed namspaces and the underscore classnames + * + */ + public function testLoadclassNamespaceMixed() + { + Web2All_Manager_Main::unregisterIncludeRoot(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); + Web2All_Manager_Main::loadClass('Web2All\\Manager\\ClassLoaderTest_F'); + $this->assertFalse(class_exists('Web2All\\Manager\\ClassLoaderTest_F', false)); + Web2All_Manager_Main::registerIncludeRoot(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); + Web2All_Manager_Main::loadClass('Web2All\\Manager\\ClassLoaderTest_F'); + $this->assertTrue(class_exists('Web2All\\Manager\\ClassLoaderTest_F', false)); + } + +} +?> \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderTest.php b/tests/Web2All/Manager/ClassLoaderTest.php new file mode 100644 index 0000000..ea542b2 --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderTest.php @@ -0,0 +1,88 @@ +assertFalse(class_exists('Web2All_Manager_ClassLoaderTest_A', true), 'Autoloading Web2All_Manager_ClassLoaderTest_A should fail'); + Web2All_Manager_Main::registerAutoloader(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); + $this->assertTrue(class_exists('Web2All_Manager_ClassLoaderTest_A', true), 'Autoloading Web2All_Manager_ClassLoaderTest_A should succeed'); + $this->assertTrue(Web2All_Manager_ClassLoaderTest_A::returnTrue()); + } + + /** + * Test loadClass + * + */ + public function testLoadClass() + { + Web2All_Manager_Main::loadClass('Web2All_Manager_ClassLoaderTest_noclass','INC'); + $this->assertTrue(class_exists('Web2All_Manager_ClassLoaderTest_noclass_A', true)); + $this->assertTrue(class_exists('Web2All_Manager_ClassLoaderTest_noclass_B', true)); + } + + /** + * Test includeClass + * + */ + public function testIncludeClassC() + { + $this->assertFalse(class_exists('Web2All_Manager_ClassLoaderTest_C', false)); + Web2All_Manager_Main::includeClass('Web2All_Manager_ClassLoaderTest_C'); + $this->assertTrue(class_exists('Web2All_Manager_ClassLoaderTest_C', false)); + } + + /** + * Test includeClass + * + */ + public function testIncludeClassD() + { + $this->assertFalse(class_exists('Web2All_Manager_ClassLoaderTest_D', false)); + Web2All_Manager_Main::includeClass('Web2All_Manager_ClassLoaderTest_D'); + $this->assertTrue(class_exists('Web2All_Manager_ClassLoaderTest_D', false)); + } + + /** + * Test loadClass with interface + * + */ + public function testLoadclassInterface() + { + Web2All_Manager_Main::loadClass('Web2All_Manager_ClassLoaderTest_TestInterface'); + $this->assertTrue(interface_exists('Web2All_Manager_ClassLoaderTest_TestInterface', false)); + // test double loading + Web2All_Manager_Main::loadClass('Web2All_Manager_ClassLoaderTest_TestInterface'); + } + +} +?> \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderTest/A.class.php b/tests/Web2All/Manager/ClassLoaderTest/A.class.php new file mode 100644 index 0000000..535514a --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderTest/A.class.php @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderTest/B.inc.php b/tests/Web2All/Manager/ClassLoaderTest/B.inc.php new file mode 100644 index 0000000..5bafcd4 --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderTest/B.inc.php @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderTest/C.php b/tests/Web2All/Manager/ClassLoaderTest/C.php new file mode 100644 index 0000000..d71707d --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderTest/C.php @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderTest/D.class.php b/tests/Web2All/Manager/ClassLoaderTest/D.class.php new file mode 100644 index 0000000..3bcfd13 --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderTest/D.class.php @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderTest/E.php b/tests/Web2All/Manager/ClassLoaderTest/E.php new file mode 100644 index 0000000..2bbc7a7 --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderTest/E.php @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderTest/F.php b/tests/Web2All/Manager/ClassLoaderTest/F.php new file mode 100644 index 0000000..229a9c9 --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderTest/F.php @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderTest/TestInterface.class.php b/tests/Web2All/Manager/ClassLoaderTest/TestInterface.class.php new file mode 100644 index 0000000..72f112e --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderTest/TestInterface.class.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/tests/Web2All/Manager/ClassLoaderTest/noclass.inc.php b/tests/Web2All/Manager/ClassLoaderTest/noclass.inc.php new file mode 100644 index 0000000..acf2eab --- /dev/null +++ b/tests/Web2All/Manager/ClassLoaderTest/noclass.inc.php @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/tests/Web2All/Manager/ManagerTest.php b/tests/Web2All/Manager/ManagerTest.php new file mode 100644 index 0000000..98639b1 --- /dev/null +++ b/tests/Web2All/Manager/ManagerTest.php @@ -0,0 +1,25 @@ +assertEquals(true, ($m instanceof Web2All_Manager_Main), 'Web2All_Manager_Main is present and instantiable'); + $this->assertEquals(0, $m->DebugLevel, 'Web2All_Manager_Main debug level should be 0 by default'); + + $c = new Web2All_Manager_Config(); + $m = new Web2All_Manager_Main($c); + $this->assertEquals(0, $m->DebugLevel, 'Web2All_Manager_Main debug level should be 0 by default'); + + $m = new Web2All_Manager_Main($c, 1); + $this->assertEquals(1, $m->DebugLevel, 'Web2All_Manager_Main debug level should be 1 by default'); + + $this->assertEquals('', $m->getIP(), 'When run from cli the getIP should return empty string'); + } + +} +?> \ No newline at end of file diff --git a/tests/Web2All/Manager/PluginInitTest.php b/tests/Web2All/Manager/PluginInitTest.php new file mode 100644 index 0000000..f6c465b --- /dev/null +++ b/tests/Web2All/Manager/PluginInitTest.php @@ -0,0 +1,120 @@ +assertTrue($web2all instanceof Web2All_Manager_Main); + return $web2all; + } + + /** + * Test instantiate a Web2All_Manager_Plugin using ->Plugin + * + * @param Web2All_Manager_Main + * @depends testManagerCreate + */ + public function testPluginExists($web2all) + { + $obj = $web2all->Plugin->Web2All_Manager_PluginInitTest_PluginExt(); + $this->assertTrue($obj->returnTrue()); + $this->assertTrue(is_string($obj->getIP())); + } + + /** + * Test instantiate a Web2All_Manager_Plugin using ->Factory + * + * @param Web2All_Manager_Main + * @depends testManagerCreate + */ + public function testFactoryPlugin($web2all) + { + $obj = $web2all->Factory->Web2All_Manager_PluginInitTest_PluginExt(); + $this->assertTrue($obj->returnTrue()); + $this->assertTrue(is_string($obj->getIP())); + } + + /** + * Test instantiate a Web2All_Manager_Plugin with constructor, using ->Factory + * + * @param Web2All_Manager_Main + * @depends testManagerCreate + */ + public function testFactoryPluginCons($web2all) + { + $obj = $web2all->Factory->Web2All_Manager_PluginInitTest_PluginExtCons('teststring'); + $this->assertTrue($obj->returnTrue()); + $this->assertTrue(is_string($obj->getIP())); + $this->assertEquals('teststring', $obj->getTest()); + } + + /** + * Test instantiate a Web2All_Manager_Plugin with constructor, using ->Plugin + * + * @param Web2All_Manager_Main + * @depends testManagerCreate + */ + public function testPluginCons($web2all) + { + $obj = $web2all->Plugin->Web2All_Manager_PluginInitTest_PluginExtCons('teststring'); + $this->assertTrue($obj->returnTrue()); + $this->assertTrue(is_string($obj->getIP())); + $this->assertEquals('teststring', $obj->getTest()); + } + + /** + * Test instantiate a simple class (non plugin) using ->Factory + * + * @param Web2All_Manager_Main + * @depends testManagerCreate + */ + public function testNonPlugin($web2all) + { + $obj = $web2all->Factory->Web2All_Manager_PluginInitTest_A(); + $this->assertTrue($obj->returnTrue()); + } + + /** + * Test instantiate a Web2All_Manager_PluginInterface using ->Factory + * + * @param Web2All_Manager_Main + * @depends testManagerCreate + */ + public function testFactoryPluginTrait($web2all) + { + $obj = $web2all->Factory->Web2All_Manager_PluginInitTest_PluginTrait(); + $this->assertTrue($obj->returnTrue()); + $this->assertTrue(is_string($obj->getIP())); + } + + /** + * Test instantiate a class using ->PluginGlobal + * + * @param Web2All_Manager_Main + * @depends testManagerCreate + */ + public function testPluginGlobal($web2all) + { + $obj = $web2all->PluginGlobal->Web2All_Manager_PluginInitTest_PluginExt(); + $this->assertTrue($obj->returnTrue()); + $obj->some_property='A'; + unset($obj); + $obj2 = $web2all->PluginGlobal->Web2All_Manager_PluginInitTest_PluginExt(); + $this->assertEquals('A', $obj2->some_property); + $obj2->some_property='B'; + $obj3 = $web2all->PluginGlobal->Web2All_Manager_PluginInitTest_PluginExt(); + $this->assertEquals('B', $obj3->some_property); + $obj4 = $web2all->Factory->Web2All_Manager_PluginInitTest_PluginExt(); + $this->assertEquals(null, $obj4->some_property); + } + +} +?> \ No newline at end of file diff --git a/tests/Web2All/Manager/PluginInitTest/A.class.php b/tests/Web2All/Manager/PluginInitTest/A.class.php new file mode 100644 index 0000000..a6f53ad --- /dev/null +++ b/tests/Web2All/Manager/PluginInitTest/A.class.php @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/tests/Web2All/Manager/PluginInitTest/PluginExt.class.php b/tests/Web2All/Manager/PluginInitTest/PluginExt.class.php new file mode 100644 index 0000000..82e6d44 --- /dev/null +++ b/tests/Web2All/Manager/PluginInitTest/PluginExt.class.php @@ -0,0 +1,14 @@ +Web2All->getIP(); + } +} +?> \ No newline at end of file diff --git a/tests/Web2All/Manager/PluginInitTest/PluginExtCons.class.php b/tests/Web2All/Manager/PluginInitTest/PluginExtCons.class.php new file mode 100644 index 0000000..db86de5 --- /dev/null +++ b/tests/Web2All/Manager/PluginInitTest/PluginExtCons.class.php @@ -0,0 +1,32 @@ +test=$test; + } + + public function returnTrue() + { + return true; + } + + public function getTest() + { + return $this->test; + } + + public function getIP() + { + return $this->Web2All->getIP(); + } +} +?> \ No newline at end of file diff --git a/tests/Web2All/Manager/PluginInitTest/PluginTrait.class.php b/tests/Web2All/Manager/PluginInitTest/PluginTrait.class.php new file mode 100644 index 0000000..95517a6 --- /dev/null +++ b/tests/Web2All/Manager/PluginInitTest/PluginTrait.class.php @@ -0,0 +1,10 @@ +Web2All->getIP(); + } +} +?> \ No newline at end of file