railpage/railpagecore

View on GitHub
lib/AppCore.php

Summary

Maintainability
F
1 wk
Test Coverage
<?php

/**
 * Railpage AppCore
 * @since Version 3.7
 * @package Railpage
 * @author Michael Greenhill
 */

namespace Railpage; 

use stdClass;
use Exception;
use Memcached as PhpMemcached;
use Redis;
use Railpage\Users\User;
use Railpage\Images\Image;
use Foolz\SphinxQL\SphinxQL;
use Foolz\SphinxQL\Connection;
use SphinxClient;
use NilPortugues\Sphinx\SphinxClient as NilPortuguesSphinxClient;

use Monolog\Logger;
use Monolog\Handler\SwiftMailerHandler;
use Monolog\Handler\PushoverHandler;
use Monolog\Formatter\HtmlFormatter;
use Swift_Message;
use Swift_Mailer;
use Swift_SmtpTransport;
use Swift_Encoding;
use Swift_Plugins_AntiFloodPlugin;
use Swift_Plugins_DecoratorPlugin;

use Doctrine\Common\Cache\MemcachedCache;
use Doctrine\Common\Cache\RedisCache;
use Railpage\Debug;

if (!defined("RP_SITE_ROOT")) {
    define("RP_SITE_ROOT", "");
}

if (!defined("RP_WEB_ROOT")) {
    define("RP_WEB_ROOT", "");
}

if (!defined("RP_HOST")) {
    define("RP_HOST", "www.railpage.com.au");
}

if (!defined("DS")) {
    define("DS", DIRECTORY_SEPARATOR);
}

/**
 * App Core
 */

class AppCore {
    
    /**
     * Database object
     * @var object $db An instance of ZendDB or sql_db representing a database connection.
     *                 sql_db is being phased out and replaced with ZendDB v1
     * @since Version 3.7
     */
    
    protected $db;
    
    /**
     * Memcache object
     * @since Version 3.7.5
     * @var object $memcache A memcache object providing get/set/update helpers.
     *                       This is being phased out in favour of getMemcacheObject(),
     *                       setMemcacheObject() and deleteMemcacheObject()
     */
    
    public $memcache;
    
    /**
     * Use cache or not
     * @since Version 3.7.5
     * @var boolean $useCache A simple yes/no for fetching objects from Memcache. Not really used
     */
     
    public $useCache = true;
    
    /**
     * Destroy
     * @since Version 3.7.5
     * @var boolean $destroy
     */
    
    public $destroy = false;
    
    /**
     * Short url / shortcut / alias to this object
     * @since Version 3.8.6
     * @var string $fwlink
     */
    
    public $fwlink;
    
    /**
     * Image object
     * 
     * Surpasses $this->photo_id and $this->Asset
     * @since Version 3.8.7
     * @var \Railpage\Images\Image $Image An instance of \Railpage\Images\Image representing the
     *                                    cover photo or dominant image of this object
     */
    
    public $Image;
    
    /**
     * Namespace
     * @since Verson 3.8.7
     * @var string $namespace The namespace of the class/object, used to standardise memcache or Image data keys
     */
    
    public $namespace;
    
    /**
     * Module information
     * @since Version 3.8.7
     * @var object $Module
     */
    
    public $Module;
    
    /**
     * StatsD object
     * @since Version 3.8.7
     * @var \stdClass $StatsD
     */
    
    public $StatsD;
    
    /**
     * User object
     * @since Version 3.9
     * @var \Railpage\Users\User $User
     */
    
    public $User;

    /**
     * Guest user object - an instance of User that represents a guest/viewer
     * @since Version 3.10.0
     * @var \Railpage\Users\User $Guest
     */

    public $Guest;

    /**
     * User object representing a recipient (eg: in an email, or a private message)
     * @since Version 3.10.0
     * @var \Railpage\Users\User $Recipient
     */

    public $Recipient;
    
    /**
     * Staff object
     * @since Version 3.9
     * @var \Railpage\Users\User $Staff
     */
    
    public $Staff;
    
    /**
     * Author object
     * @since Version 3.9
     * @var \Railpage\Users\User $Author
     */
    
    public $Author;
    
    /**
     * Constructor
     * @since Version 3.7
     * @global object $ZendDB
     * @global object $ZendDB_ReadOnly
     * @throws \Exception if a database object cannot be created
     */
    
    public function __construct() {
        global $ZendDB, $ZendDB_ReadOnly, $PHPUnitTest;
        
        /**
         * Create the registry
         */
        
        $Registry = Registry::getInstance();
        
        $this->log = self::getLogger();
        
        if (isset($PHPUnitTest) && $PHPUnitTest === true) {
            
            try {
                $this->db = $Registry->get("db_phpunit");
            } catch (Exception $e) {
                require("db.dist" . DS . "zend_db.php"); 
                $this->db = $ZendDB;
                $this->destroy = true;
                
                $Registry->set("db_phpunit", $this->db);
            }
            
            $ZendDB_ReadOnly = $this->db;
            $this->setDatabaseReadOnlyConnection($this->db); 
            
        } else {
            
            /**
             * Load / set the database instance
             */
            
            try {
                $this->db = $Registry->get("db");
            } catch (Exception $e) {
                
                if (isset($ZendDB)) {
                    $this->db = $ZendDB; 
                } elseif (file_exists("db" . DS . "zend_db.php")) {
                    Debug::logEvent(__METHOD__ . " -- Looking for db " . DS . "zend_db.php");
                    require("db" . DS . "zend_db.php"); 
                    $this->db = $ZendDB;
                    $this->destroy = true;
                } elseif (file_exists(".." . DS . "db" . DS . "zend_db.php")) {
                    Debug::logEvent(__METHOD__ . " -- Looking for .." . DS . "db " . DS . "zend_db.php");
                    require(".." . DS . "db" . DS . "zend_db.php"); 
                    $this->db = $ZendDB;
                    $this->destroy = true;
                } else {
                    
                    // Attempt to resolve the DB connection to a stream path. If it can't be resolved, assume this is a CI environment and load a test DB connector
                    
                    Debug::logEvent(__METHOD__ . " -- Looking for db " . DS . "connect.php");
                    
                    if (stream_resolve_include_path("db" . DS . "connect.php")) {
                        require("db" . DS . "connect.php"); 
                        throw new Exception(__CLASS__." needs a database object");
                        $this->db = $db;
                        $this->destroy = true;
                    }
                }
                
                $Registry->set("db", $this->db);
            }
            
            /**
             * Load / set the read-only database instance
             */
            
            try {
                $this->db_readonly = $Registry->get("db_readonly");
            } catch (Exception $e) {
                if (isset($ZendDB_ReadOnly)) {
                    $this->db_readonly = $ZendDB_ReadOnly; 
                } elseif (file_exists("db" . DS . "zend_db.php")) {
                    require("db" . DS . "zend_db.php"); 
                    $this->db_readonly = $ZendDB_ReadOnly;
                    $this->destroy = true;
                } elseif (file_exists(".." . DS . "db" . DS . "zend_db.php")) {
                    require(".." . DS . "db" . DS . "zend_db.php"); 
                    $this->db_readonly = $ZendDB_ReadOnly;
                    $this->destroy = true;
                }
                
                $Registry->set("db_readonly", $this->db_readonly);
            }
        }
        
        /** 
         * Create Memcache object
         */
        
        $this->memcache = self::getMemcache(); 
        
        /**
         * Build the StatsD object
         */
        
        try {
            $this->StatsD = $Registry->get("statsd"); 
        } catch (Exception $e) {
            $this->StatsD = new stdClass;
            $this->StatsD->target = new stdClass;
            $Registry->set("statsd", $this->StatsD);
        }
        
        /**
         * Load the config
         */
        
        $this->Config = self::getConfig();
        
        /**
         * Load / set the Memcached object
         */
        
        $this->Memcached = self::getMemcached();
        
        /**
         * Load / set the Redis object
         */
        
        $this->Redis = self::getRedis();
    }
    
    /**
     * Deconstructor
     *
     * Close connections to the database
     * @since Version 3.7
     */
    
    protected function __deconstruct() {
        if ($this->destroy) {
            if ($this->db instanceof \sql_db) {
                $this->db->close(); 
            } else {
                #$this->closeConnection(); 
            }
        }
    }
    
    /**
     * Cache an object in Memcache
     * @deprecated 3.8.7 Calls to this method should be replaced with setMemcacheObject() with the same parameter(s)
     * @since Version 3.7.5
     * @param string|boolean $key Memcache's unique identifier for this data
     * @param mixed $value The data we wish to store in Memcache
     * @param int $exp A unix timestamp representing the expiry point. Leave as 0 for never expire
     * @return boolean
     */
    
    protected function setCache($key = false, $value = "thisisanemptyvalue", $exp = 0) {
        if (!$this->useCache) {
            return false;
        }
        
        return setMemcacheObject($key, $value, $exp); 
    }
    
    /**
     * Fetch an object from Memcache
     * @deprecated 3.8.7 Calls to this method should be replaced with getMemcacheObject() with the same parameter(s)
     * @since Version 3.7.5
     * @param string|boolean $key
     * @return mixed
     */
    
    protected function getCache($key = false) {
        if (!$this->useCache) {
            return false;
        }
        
        return getMemcacheObject($key);
    }
    
    /**
     * Remove an object from Memcache
     * @deprecated 3.8.7 Calls to this method should be replaced with deleteMemcacheObject() with the same parameter(s)
     * @since Version 3.7.5
     * @param string|boolean $key
     * @return mixed
     */
    
    protected function removeCache($key = false) {
        if (!$this->useCache) {
            return false;
        }
        
        return removeMemcacheObject($key);
    }
    
    /**
     * Remove an object from Memcache
     * @deprecated 3.8.7 Calls to this method should be replaced with deleteMemcacheObject() with the same parameter(s)
     * @since Version 3.7.5
     * @param string|boolean $key
     * @return mixed
     */
    
    protected function deleteCache($key = false) {
        if (!$this->useCache) {
            return false;
        }
        
        return removeMemcacheObject($key); 
    }
    
    /**
     * Set the user for this object
     * @since Version 3.8.7
     * @param \Railpage\Users\User $userObject
     * @return $this
     */
    
    public function setUser(User $userObject) {
        
        $this->User = $userObject;
        
        return $this;
        
    }
    
    /**
     * Set the author for this object
     * @since Version 3.8.7
     * @param \Railpage\Users\User $userObject
     * @return $this
     */
    
    public function setAuthor(User $userObject) {
        
        $this->Author = $userObject;
        
        return $this;
        
    }
    
    /**
     * Set the staff user object for this object
     * @since Version 3.8.7
     * @param \Railpage\Users\User $userObject
     * @return $this
     */
    
    public function setStaff(User $userObject) {
        
        $this->Staff = $userObject;
        
        return $this;
        
    }
    
    /**
     * Set the object string for this object
     * @since Version 3.8.7
     * @param object $object
     * @return $this
     */
    
    public function setObject($object) {
        
        if (is_object($object)) {
            $this->object = get_class($object);
        }
        
        return $this;
        
    }
    
    /**
     * Set the guest user for this object
     * @since Version 3.8.7
     * @param \Railpage\Users\User $userObject
     * @return $this
     */
    
    public function setGuest(User $userObject) {
        
        $this->Guest = $userObject;
        
        return $this;
        
    }
    
    /**
     * Set the recipient for this object
     * @since Version 3.8.7
     * @param \Railpage\Users\User $userObject
     * @return $this
     */
    
    public function setRecipient(User $userObject) {
        
        $this->Recipient = $userObject;
        
        return $this;
        
    }
    
    /**
     * Create and connect to SphinxQL instance
     * @since Version 3.9.0
     * @return \Foolz\SphinxQL\SphinxQL
     */
    
    public static function getSphinx() {
        
        $Config = self::getConfig();
        
        $conn = new Connection();
        $conn->setParams(array("host" => $Config->Sphinx->Host, "port" => $Config->Sphinx->Port));
        
        return SphinxQL::create($conn);
        
    }
    
    /**
     * Create and connect to the Sphinx API
     * @since Version 3.9.1
     * @return \NilPortugues\Sphinx\SphinxClient
     */
    
    public static function getSphinxAPI() {
        
        $Config = self::getConfig(); 
        $Sphinx = new NilPortuguesSphinxClient;
        
        if (class_exists("SphinxClient")) {
            $Sphinx = new SphinxClient;
        }
        
        $Sphinx->setServer($Config->Sphinx->Host, 9306);
        $Sphinx->setMatchMode(SPH_MATCH_ALL);
        $Sphinx->setMaxQueryTime(300);
        
        return $Sphinx;
        
    }
    
    /**
     * Get RP configuration
     * @since Version 3.9.1
     * @return \stdClass
     */
    
    public static function getConfig() {
        $Registry = Registry::getInstance();
        
        try {
            $Config = $Registry->get("config");
        } catch (Exception $e) {
            if (function_exists("getRailpageConfig")) {
                Debug::logEvent(__METHOD__ . " -- found getRailpageConfig()");
                $Config = getRailpageConfig();
            } elseif (defined("RP_SITE_ROOT") && file_exists(RP_SITE_ROOT . DS . "config" . DS . "config.railpage.json")) {
                Debug::logEvent(__METHOD__ . " -- Loading " . RP_SITE_ROOT . DS . "config" . DS . "config.railpage.json");
                $Config = json_decode(file_get_contents(RP_SITE_ROOT . DS . "config" . DS . "config.railpage.json")); 
            } elseif (file_exists(dirname(__DIR__) . DS . "config.railpage.json")) {
                Debug::logEvent(__METHOD__ . " -- Loading " . dirname(__DIR__) . DS . "config.railpage.json");
                $Config = json_decode(file_get_contents(dirname(__DIR__) . DS . "config.railpage.json"));
            } elseif (file_exists(dirname(__DIR__) . DS . "config" . DS . "dist.config.railpage.php")) {
                Debug::logEvent(__METHOD__ . " -- Loading " . dirname(__DIR__) . DS . "config" . DS . "dist.config.railpage.php");
                require_once(dirname(__DIR__) . DS . "config" . DS . "dist.config.railpage.php");
                $Config = $RailpageConfig;
            }
            
            if (!isset($Config)) {
                $Config = array(
                    "Memcached" => array(
                        "Host" => "cache.railpage.com.au",
                        "Port" => 11211
                    )
                );
                
                $Config = json_decode(json_encode($Config));
            }
            
            $Registry->set("config", $Config);
        }
        
        return $Config;
    }
     
    
    /**
     * Get our Redis instance
     * @since Version 3.9.1
     * @return \Doctrine\Common\Cache\RedisCache
     * @param boolean $reload Don't fetch the cache handler from the registry and instead create a new instance
     */
    
    public static function getRedis($reload = false) {
        if (!extension_loaded("redis") || (defined("NOREDIS") && NOREDIS == true)) {
            return new NullCacheDriver;
        }
        
        if (defined("PHPUNIT_RAILPAGE_TESTSUITE")) {
            return new NullCacheDriver;
        }
        
        $Registry = Registry::getInstance();
        
        $Config = self::getConfig();
        
        if ($reload) {
            $Registry->remove("redis");
        }
        
        try {
            $cacheDriver = $Registry->get("redis");
        } catch (Exception $e) {
            $Redis = new Redis;
            $Redis->connect($Config->Memcached->Host, 6379);
            
            $cacheDriver = new RedisCache;
            $cacheDriver->setRedis($Redis);
            
            $Registry->set("redis", $cacheDriver);
        }
        
        return $cacheDriver;
    }
    
    /**
     * Get an instance of Memcache for legacy code
     * @since Version 3.9.1
     * @return object
     * @param boolean $reload Don't fetch the cache handler from the registry and instead create a new instance
     */
    
    public static function getMemcache($reload = false) {
        
        if (!extension_loaded("memcache") || defined("PHPUNIT_RAILPAGE_TESTSUITE")) {
            return new NullCacheDriver;
        }
        
        $Registry = Registry::getInstance();
        
        if ($reload) {
            $Registry->remove("memcache");
        }
        
        try {
            $memcache = $Registry->get("memcache");
        } catch (Exception $e) {
            
            $memcache = false;

            Debug::logEvent(__METHOD__ . " -- Looking for memcache.php");
            
            if (file_exists(__DIR__ . DIRECTORY_SEPARATOR . "memcache.php")) {
                require(__DIR__ . DIRECTORY_SEPARATOR . "memcache.php"); 
            } 
            
            $Registry->set("memcache", $memcache); 
        }
        
        return $memcache;
        
    }
    
    /**
     * Get our Memcached instance
     * @since Version 3.9.1
     * @return \Doctrine\Common\Cache\MemcachedCache
     */
    
    public static function getMemcached() {
        
        if (!extension_loaded("memcached") || defined("PHPUNIT_RAILPAGE_TESTSUITE")) {
            return new NullCacheDriver;
        }
        
        $Registry = Registry::getInstance();
        
        $Config = self::getConfig();
        
        try {
            $cacheDriver = $Registry->get("memcached");
        } catch (Exception $e) {
            $Memcached = new PhpMemcached;
            $Memcached->addServer($Config->Memcached->Host, 11211);
            
            $cacheDriver = new MemcachedCache;
            $cacheDriver->setMemcached($Memcached);
            
            $Registry->set("memcached", $cacheDriver);
        }
        
        return $cacheDriver;
    }
    
    /**
     * Create a URL slug if the create_slug() function doesn't exist
     * @since Version 3.9.1
     * @param string $string
     * @return string
     */
    
    public static function create_slug($string) {
        
        if (function_exists("create_slug")) {
            return create_slug($string); 
        }
        
        $find = array(
            "(",
            ")",
            "-"
        );
        
        $replace = array_fill(0, count($find), ""); 
        
        $string = str_replace($find, $replace, $string);
            
        $slug = strtolower(preg_replace('/[^A-Za-z0-9-]+/', '-', trim($string)));
        return $slug;
        
    }
    
    /**
     * Set the database connection for this object
     * @since Version 3.9.1
     * @return $this
     * @throws \Exception if no database connection is provided
     */
    
    public function setDatabaseConnection($cn = false) {
        if (!is_object($cn)) {
            throw new Exception("Invalid database connection specified");
        }
        
        $this->db = $cn;
        
        return $this;
    }
    
    /**
     * Set the read-only database connection for this object
     * @since Version 3.9.1
     * @return $this
     * @throws \Exception if no database connection is provided
     */
    
    public function setDatabaseReadOnlyConnection($cn = false) {
        if (!is_object($cn)) {
            throw new Exception("Invalid database connection specified");
        }
        
        $this->db_readonly = $cn;
        
        return $this;
    }
    
    /**
     * Get the logger interface
     * @since Version 3.9.1
     * @return \Monolog\Logger
     */
    
    public static function getLogger() {
        
        $Registry = Registry::getInstance();
        
        $Config = self::getConfig();
        
        try {
            $Log = $Registry->get("log");
        } catch (Exception $e) {
            $Log = new Logger("railpage");
            
            /**
             * Shit's broke, send an email via SwiftMail
             */
            
            if (isset($Config->SMTP)) {
                
                $Redis = self::getRedis(); 
                
                if (!$mailStream = $Redis->fetch("railpage:logger.swiftmail")) {
                
                    $transport = Swift_SmtpTransport::newInstance($Config->SMTP->host, $Config->SMTP->port, $Config->SMTP->TLS = true ? "tls" : NULL)
                        ->setUsername($Config->SMTP->username)
                        ->setPassword($Config->SMTP->password);
                        
                    $mailer = Swift_Mailer::newInstance($transport);
                    $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30));
                    
                    $message = Swift_Message::newInstance("[RP] Something went wrong!")
                        ->setFrom(array("errorz@railpage.com.au" => "Failpage service"))
                        ->setTo(array("michael@railpage.com.au" => "Michael Greenhill"))
                        ->setBody("", "text/html");
                    
                    $htmlFormatter = new HtmlFormatter;
                    
                    $mailStream = new SwiftMailerHandler($mailer, $message, Logger::ERROR);
                    $mailStream->setFormatter($htmlFormatter);
                    
                    $Redis->save("railpage:logger.swiftmail", $mailStream); 
                
                }
                
                $Log->pushHandler($mailStream);
            }
            
            /**
             * Pushover
             */
            
            #$Log->pushHandler(new PushoverHandler("aVHTDYcWLH7y8ZXoDNnaHjAuH7gnY5", "uWy9phgETHeYLqTZBL5YSVjoqB93id", "Railpage API"));
            
            $Registry->set("log", $Log);
        }
        
        return $Log;
    }
    
    /**
     * Get alert-to-email object
     * @since Version 3.9.1
     * @return \Monolog\Logger
     */
    
    public static function getAlerter() {
        
        $Registry = Registry::getInstance();
        
        try {
            $Monolog = $Registry->get("alerter"); 
        } catch (Exception $e) {
            $Monolog = new Logger("railpage"); 
            #$Monolog->pushHandler(new SwiftMailerHandler(
        }
        
        return $Monolog;
    }
    
    /**
     * Get our smarty template engine
     * @since Version 3.9.1
     * @return \Railpage\Template
     */
    
    public static function getSmarty() {
        
        $Registry = Registry::getInstance(); 
        
        $Config = self::getConfig(); 
        
        try {
            $Smarty = $Registry->get("smarty"); 
        } catch (Exception $e) {
            $Smarty = new Template;
            
            /**
             * Send the RP config to Smarty
             */
            
            $Smarty->setRailpageConfig($Config);
            
            /**
             * Smarty options and configuration
             */
            
            $Smarty->debugging      = false;
            $Smarty->caching        = false;
            $Smarty->cache_lifetime = 120;
            $Smarty->site_root      = RP_SITE_ROOT; 
            $Smarty->use_sub_dirs   = true;
            $Smarty->assign("rp_host", RP_HOST);
            $Smarty->assign("rand", rand());
            $Smarty->assign("rp_version", defined("RP_VERSION") ? RP_VERSION : "0.0.0");
            $Smarty->assign("rp_web_root", defined("RP_WEB_ROOT") ? RP_WEB_ROOT : __DIR__);
            $Smarty->assign("site_name", $Config->SiteName);
            $Smarty->assign("rp_production", (defined("RP_ISPRODUCTION") ? "RP_ISPRODUCTION" : define("RP_ISPRODUCTION", true)) ? true : false);
            $Smarty->assign("ad_header", $Config->AdHeader);
            $Smarty->assign("rp_map_styles", json_encode($Config->Google->Maps->Styles));
            $Smarty->assign("rp_map_icons", json_encode($Config->Google->Maps->Icons));
            $Smarty->subtheme = "smooth";
            
            $Smarty->Assign("rp_ui_show_banner_head", true);
            
            /**
             * Set the template compile directory
             */
            
            $Smarty->setCompileDir(dirname(RP_SITE_ROOT) . DS . "smarty" . DS . "compile");
            
            $Registry->set("smarty", $Smarty);
            
        }
        
        return $Smarty;
    }
    
    /**
     * Return the database connection
     * @since Version 3.9.1
     * @return \Zend_Db
     */
    
    public function getDatabaseConnection() {
        return $this->db;
    }
    
    /**
     * Get the database connection in a super lazy coding way
     * @since Version 3.10.0
     * @return \Zend_Db
     */
    
    public static function GetDatabase() {
        
        return (new AppCore)->getDatabaseConnection(); 
        
    }
    
}