config<\/strong> folder.<\/p>\n\n\n\n<?php\n\nreturn [\n 'env' => 'production',\n \/\/ values for mail_driver: smtp, mandrill, array\n 'mail_driver' => 'smtp',\n 'mandrill_key' => 'mandrill-key',\n 'mail_host' => 'smtp.mailtrap.io',\n 'mail_port' => '2525',\n 'mail_username' => 'username',\n 'mail_password' => 'password',\n 'mail_from_address' => 'info@app.com',\n 'mail_to_admin' => 'admin@admin.com'\n];<\/pre>\n\n\n\nThe configuration file defines values for application environment, mail driver, and SMTP and API credentials for the mailers. In order to use the configuration values in the application, we will use config class rather than a global config variable. Below is the code for the Config.php <\/em>class.<\/p>\n\n\n\n<?php \n\nnamespace Apr\\ExceptionHandling;\n\nabstract class Config\n{\n private static $config;\n\n private static function getConfig() {\n\n if (self::$config) {\n return self::$config;\n }\n \n return self::$config = require __DIR__ . '\/..\/config\/config.php';\n \n }\n\n public static function get($property) \n {\n if (!array_key_exists($property, self::getConfig())) {\n return;\n }\n\n return self::getConfig()[$property];\n }\n}<\/pre>\n\n\n\nConfig.php<\/em> is a utility helper class that wraps configuration file. Now we can get values from configuration file by statically calling get function. For example to get ‘env’ variable we can call Config::get(‘env’).<\/em><\/p>\n\n\n\nA similar helper Log.php<\/em> class wraps an instance of Monolog logger. <\/p>\n\n\n\n<?php \n\nnamespace Apr\\ExceptionHandling;\n\nuse DateTime;\nuse Monolog\\Logger;\nuse Monolog\\Handler\\StreamHandler;\nuse Monolog\\Formatter\\LineFormatter;\n\nabstract class Log\n{\n private static $logger;\n\n private static function getLogger()\n {\n if (self::$logger) {\n return self::$logger;\n }\n\n $logger = new Logger(Config::get('env'));\n\n $formatter = new LineFormatter(null, null, false, true);\n $now = (new DateTime(\"now\"))->format('m_d_Y');\n\n $handler = new StreamHandler(__DIR__ . \"\/..\/logs\/app_log_$now.log\", Logger::INFO);\n $handler->setFormatter($formatter);\n\n $logger->pushHandler($handler);\n\n return self::$logger = $logger;\n }\n\n public static function __callStatic($name, $arguments)\n {\n if (empty($arguments)) {\n throw new \\InvalidArgumentException(\"There is no message to log\");\n }\n\n $message = $arguments[0];\n\n $logger = self::getLogger();\n\n $logger->$name($message);\n }\n}<\/pre>\n\n\n\nThe above class creates an instance of Monolog logger and uses __callStatic magic method to dynamically call logging methods on the logger. So, we can use Log::info(‘Logging…’) or Log::alert(‘Error…’).<\/em> You can also create another stream handler to log errors in a separate error log file. Another thing to note is that if no message is passed to a logging function, InvalidArgumentException <\/em>will be thrown.<\/p>\n\n\n\nNow it is a good time to look at index.php file <\/em>located in public folder.<\/p>\n\n\n\n<?php\n\nrequire __DIR__ . '\/..\/bootstrap.php';\n\nuse Apr\\ExceptionHandling\\Log;\n\nLog::info('test log');\n\nLog::info();\n\necho 'Hello';<\/pre>\n\n\n\nindex.php<\/em> file contains a simple sample code that first will write a log entry in our log file and them attempt to illegally use Log::info()<\/em> method that will cause InvalidArgumentException<\/em> to be thrown. Since the code does not user try..catch<\/em> block, this exception will bubble up and its handling will take place in bootstrap.php<\/em> file.<\/p>\n\n\n\n<?php\n\nrequire __DIR__ . '\/vendor\/autoload.php';\n\nuse Apr\\ExceptionHandling\\Config;\nuse Apr\\ExceptionHandling\\Exceptions\\ExceptionHandler;\n\n$whoops = new \\Whoops\\Run;\n\nif (Config::get('env') === 'develop') {\n $whoops->pushHandler(new \\Whoops\\Handler\\PrettyPageHandler);\n}\n\n$whoops->pushHandler(function ($exception, $inspector, $run) {\n $exceptionHandler = new ExceptionHandler;\n $exceptionHandler->report($exception);\n});\n\n$whoops->register();<\/pre>\n\n\n\nBesides requiring composer’s autoload.php<\/em> file, bootsrtap.php<\/em> also contains logic that will use Whoop’s PrettyPageHandler<\/em> to display a nice exception page below.<\/p>\n\n\n\nWhoop’s exception page<\/figcaption><\/figure><\/div>\n\n\n\nThe code will also use our custom ExceptionHandler<\/em> class to report the exception. Let’s take a look at that class.<\/p>\n\n\n\n<?php\n\nnamespace Apr\\ExceptionHandling\\Exceptions;\n\nuse Apr\\ExceptionHandling\\Log;\nuse Apr\\ExceptionHandling\\Config;\nuse Apr\\ExceptionHandling\\Mailer\\Mail;\n\nclass ExceptionHandler \n{\n public function report($exception) \n {\n Log::error($exception);\n\n if (Config::get('env') === 'production') \n {\n Mail::to(Config::get('mail_to_admin'))\n ->from(Config::get('mail_from_address'))\n ->subject('Exception occured')\n ->message($exception)\n ->send();\n \n throw $exception;\n \/\/ or display 500 error page\n }\n }\n}<\/pre>\n\n\n\nExceptionHandler::report()<\/em> method logs “bubbled up” exception and if the application in production emails it to the site administrator. Mail<\/em> class has a fluent interface to create an email and mail the exception.<\/p>\n\n\n\n<?php\n\nnamespace Apr\\ExceptionHandling\\Mailer;\n\nuse Apr\\ExceptionHandling\\Log;\n\nclass Mail\n{\n public $to;\n public $from;\n public $subject = 'Email Notification';\n public $message;\n\n public static function to($addresses) \n {\n if (!is_array($addresses)) {\n $to = [$addresses];\n } else {\n $to = $addresses;\n }\n\n $instance = new self;\n $instance->to = $to;\n return $instance;\n }\n\n public function from($addresses) \n {\n if (!is_array($addresses)) {\n $this->from = [$addresses];\n } else {\n $this->from = $addresses;\n }\n\n return $this;\n }\n\n public function message($message)\n {\n $this->message = $message;\n return $this;\n }\n\n public function subject($subject)\n {\n $this->subject = $subject;\n return $this;\n }\n\n public function send()\n {\n $mailer = new Mailer;\n\n try {\n $mailer->send($this);\n } catch (\\Throwable $e) {\n Log::error($e);\n }\n\n \n }\n}<\/pre>\n\n\n\nMail::to()<\/em> method creates an instance of Mail<\/em> class and each chained method changes class properties and returns the instance back. Finally Mail::send()<\/em> creates an instance of a mailer and sends the email notification.<\/p>\n\n\n\n<?php\n\nnamespace Apr\\ExceptionHandling\\Mailer;\n\nuse Apr\\ExceptionHandling\\Config;\nuse Apr\\ExceptionHandling\\Log;\n\nclass Mailer\n{\n public function send($mail)\n {\n if (Config::get('mail_driver') === 'smtp') {\n\n $transport = (new \\Swift_SmtpTransport(Config::get('mail_host'), (int) Config::get('mail_port')))\n ->setUsername(Config::get('mail_username'))\n ->setPassword(Config::get('mail_password'))\n ;\n\n \/\/ Create the Mailer using your created Transport\n $mailer = new \\Swift_Mailer($transport);\n\n \/\/ Create a message\n $message = (new \\Swift_Message($mail->subject))\n ->setFrom($mail->from)\n ->setTo($mail->to)\n ->setBody($mail->message)\n ;\n\n return $mailer->send($message);\n\n } else if (Config::get('mail_driver') === 'mandrill') {\n \n $mandrill = new \\Mandrill(Config::get('mandrill_key'));\n\n $to = [];\n foreach ($mail->to as $address) {\n $to[] = ['email' => $address]; \n }\n\n $message = [\n 'html' => \"<p>$mail->message<\/p>\",\n 'text' => $mail->message,\n 'subject' => $mail->subject,\n 'from_email' => $mail->from[0],\n 'to' => $to,\n ];\n\n $async = false;\n $ip_pool = 'Main Pool';\n $send_at = (new \\DateTime(\"now\"))->format('Y-m-d H:m:s');\n return $mandrill->messages->send($message, $async, $ip_pool, $send_at); \n }\n\n Log::info(json_encode($mail));\n }\n}<\/pre>\n\n\n\nMailer::send()<\/em> method depending on the configuration mail driver creates either SwiftMailer or Mandrill instance and sends out the mail. If the driver is not specified, or other than ‘mandrill’ or ‘smtp’, the method will json_encode<\/em> mail and log it to the log file. <\/p>\n\n\n\nIt is also worth noting that Monolog has SwiftMailHandler. It is possible to configure the logger it in such a way that Log::critical(‘Error message..’)<\/em> will also email the exception. Although this approach may be more convenient, creating a separate mailer, to my mind, gives greater flexibility in choosing when to mail exceptions.<\/p>\n\n\n\nTo summarize, we created a skeleton for a PHP application that has exception handling, logging and email features. <\/p>\n","protected":false},"excerpt":{"rendered":"
When setting up a new PHP application one should consider how to manage certain housekeeping tasks such as exception handling, logging, and mail notifications.<\/p>\n","protected":false},"author":1,"featured_media":6122,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"footnotes":""},"categories":[10],"tags":[],"class_list":["post-6118","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-php-mysql-development"],"yoast_head":"\n
Setting up a PHP Application | Alex Rusin Blog<\/title>\n \n \n \n \n \n \n \n \n \n \n \n\t \n\t \n\t \n \n \n \n\t \n\t \n\t \n