diff --git a/oss/piwik/trunk/core/Db/Adapter.php b/oss/piwik/trunk/core/Db/Adapter.php
new file mode 100644
index 00000000..d7b6aa15
--- /dev/null
+++ b/oss/piwik/trunk/core/Db/Adapter.php
@@ -0,0 +1,166 @@
+getConnection();
+
+ Zend_Db_Table::setDefaultAdapter($adapter);
+
+ // we don't want the connection information to appear in the logs
+ $adapter->resetConfig();
+
+ return $adapter;
+ }
+
+ /**
+ * Get adapter class name
+ *
+ * @param string $adapterName
+ * @return string
+ */
+ private static function getAdapterClassName($adapterName)
+ {
+ return 'Piwik_Db_Adapter_' . str_replace(' ', '_', ucwords(str_replace('_', ' ', strtolower($adapterName))));
+ }
+
+ /**
+ * Get default port for named adapter
+ *
+ * @param string $adapterName
+ * @return int
+ */
+ public static function getDefaultPortForAdapter($adapterName)
+ {
+ $className = self::getAdapterClassName($adapterName);
+ return call_user_func(array($className, 'getDefaultPort'));
+ }
+
+ /**
+ * Get list of adapters
+ *
+ * @return array
+ */
+ public static function getAdapters()
+ {
+ static $adapterNames = array(
+ // currently supported by Piwik
+ 'Pdo_Mysql',
+ 'Mysqli',
+
+ // other adapters supported by Zend_Db
+// 'Pdo_Pgsql',
+// 'Pdo_Mssql',
+// 'Sqlsrv',
+// 'Pdo_Ibm',
+// 'Db2',
+// 'Pdo_Oci',
+// 'Oracle',
+ );
+
+ $adapters = array();
+
+ foreach($adapterNames as $adapterName)
+ {
+ $className = 'Piwik_Db_Adapter_'.$adapterName;
+ if(call_user_func(array($className, 'isEnabled')))
+ {
+ $adapters[strtoupper($adapterName)] = call_user_func(array($className, 'getDefaultPort'));
+ }
+ }
+
+ return $adapters;
+ }
+}
+
+/**
+ * @package Piwik
+ * @subpackage Piwik_Db
+ */
+interface Piwik_Db_Adapter_Interface
+{
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig();
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort();
+
+ /**
+ * Check database server version
+ *
+ * @throws Exception if database version is less than required version
+ */
+ public function checkServerVersion();
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled();
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType();
+
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno);
+
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ public function isConnectionUTF8();
+}
diff --git a/oss/piwik/trunk/core/Db/Adapter/Mysqli.php b/oss/piwik/trunk/core/Db/Adapter/Mysqli.php
new file mode 100644
index 00000000..0c7fe220
--- /dev/null
+++ b/oss/piwik/trunk/core/Db/Adapter/Mysqli.php
@@ -0,0 +1,156 @@
+_config = array();
+ }
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 3306;
+ }
+
+ /**
+ * Check MySQL version
+ */
+ public function checkServerVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $requiredVersion = Zend_Registry::get('config')->General->minimum_mysql_version;
+ if(version_compare($serverVersion, $requiredVersion) === -1)
+ {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
+ }
+ }
+
+ /**
+ * Check client version compatibility against database server
+ */
+ public function checkClientVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $clientVersion = $this->getClientVersion();
+ if(version_compare($serverVersion, '5') >= 0
+ && version_compare($clientVersion, '5') < 0)
+ {
+ throw new Exception(Piwik_TranslateException('General_ExceptionIncompatibleClientServerVersions', array('MySQL', $clientVersion, $serverVersion)));
+ }
+ }
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('mysqli', $extensions);
+ }
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ return true;
+ }
+
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ if(is_null($this->_connection))
+ {
+ if(preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match))
+ {
+ return $match[1] == $errno;
+ }
+ return mysqli_connect_errno() == $errno;
+ }
+
+ return mysqli_errno($this->_connection) == $errno;
+ }
+
+ /**
+ * Execute unprepared SQL query and throw away the result
+ *
+ * Workaround some SQL statements not compatible with prepare().
+ * See http://framework.zend.com/issues/browse/ZF-1398
+ *
+ * @param string $sqlQuery
+ * @return int Number of rows affected (SELECT/INSERT/UPDATE/DELETE)
+ */
+ public function exec( $sqlQuery )
+ {
+ $rc = mysqli_query($this->_connection, $sqlQuery);
+ $rowsAffected = mysqli_affected_rows($this->_connection);
+ if(!is_bool($rc))
+ {
+ mysqli_free_result($rc);
+ }
+ return $rowsAffected;
+ }
+
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ public function isConnectionUTF8()
+ {
+ $charset = mysqli_character_set_name($this->_connection);
+ return $charset === 'utf8';
+ }
+
+ /**
+ * Get client version
+ *
+ * @return string
+ */
+ public function getClientVersion()
+ {
+ $this->_connect();
+ $version = $this->_connection->server_version;
+ $major = (int) ($version / 10000);
+ $minor = (int) ($version % 10000 / 100);
+ $revision = (int) ($version % 100);
+ return $major . '.' . $minor . '.' . $revision;
+ }
+}
diff --git a/oss/piwik/trunk/core/Db/Adapter/Pdo/Mssql.php b/oss/piwik/trunk/core/Db/Adapter/Pdo/Mssql.php
new file mode 100644
index 00000000..7246c383
--- /dev/null
+++ b/oss/piwik/trunk/core/Db/Adapter/Pdo/Mssql.php
@@ -0,0 +1,253 @@
+_connection)
+ {
+ return $this->_connection;
+ }
+
+ $this->_pdoType = "sqlsrv";
+ // get the dsn first, because some adapters alter the $_pdoType
+ //$dsn = $this->_dsn();
+
+ // check for PDO extension
+ if (!extension_loaded('pdo'))
+ {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ throw new Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
+ }
+
+ // check the PDO driver is available
+ if (!in_array($this->_pdoType, PDO::getAvailableDrivers()))
+ {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ throw new Zend_Db_Adapter_Exception('The ' . $this->_pdoType . ' driver is not currently installed');
+ }
+
+ // create PDO connection
+ $q = $this->_profiler->queryStart('connect', Zend_Db_Profiler::CONNECT);
+
+ // add the persistence flag if we find it in our config array
+ if (isset($this->_config['persistent']) && ($this->_config['persistent'] == true))
+ {
+ $this->_config['driver_options'][PDO::ATTR_PERSISTENT] = true;
+ }
+
+ try {
+ $serverName = $this->_config["host"];
+ $database = $this->_config["dbname"];
+ if(is_null($database))
+ {
+ $database = 'master';
+ }
+ $uid = $this->_config['username'];
+ $pwd = $this->_config['password'];
+ if($this->_config["port"] != "")
+ {
+ $serverName = $serverName.",".$this->_config["port"];
+ }
+
+ $this->_connection = new PDO( "sqlsrv:$serverName", $uid, $pwd, array( 'Database' => $database ));
+
+
+ if( $this->_connection === false )
+ {
+ die( self::FormatErrors( sqlsrv_errors() ) );
+ }
+
+ /*
+ $this->_connection = new PDO(
+ $dsn,
+ $this->_config['username'],
+ $this->_config['password'],
+ $this->_config['driver_options']
+ );
+ */
+
+ $this->_profiler->queryEnd($q);
+
+ // set the PDO connection to perform case-folding on array keys, or not
+ $this->_connection->setAttribute(PDO::ATTR_CASE, $this->_caseFolding);
+ $this->_connection->setAttribute(PDO::SQLSRV_ENCODING_UTF8, true);
+
+
+ // always use exceptions.
+ $this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+ return $this->_connection;
+ } catch (PDOException $e) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ throw new Zend_Db_Adapter_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig()
+ {
+ $this->_config = array();
+ }
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 1433;
+ }
+
+ /**
+ * Check MSSQL version
+ */
+ public function checkServerVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $requiredVersion = Zend_Registry::get('config')->General->minimum_mssql_version;
+ if(version_compare($serverVersion, $requiredVersion) === -1)
+ {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MSSQL', $serverVersion, $requiredVersion)));
+ }
+
+ }
+
+ public function getServerVersion()
+ {
+ try
+ {
+ $stmt = $this->query("SELECT CAST(SERVERPROPERTY('productversion') as VARCHAR) as productversion");
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+ if (count($result))
+ {
+ return $result[0][0];
+ }
+ }
+ catch (PDOException $e)
+ {
+ }
+
+ return null;
+ }
+
+ /**
+ * Check client version compatibility against database server
+ */
+ public function checkClientVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $clientVersion = $this->getClientVersion();
+ if(version_compare($serverVersion, '10') >= 0
+ && version_compare($clientVersion, '10') < 0)
+ {
+ throw new Exception(Piwik_TranslateException('General_ExceptionIncompatibleClientServerVersions', array('MSSQL', $clientVersion, $serverVersion)));
+ }
+ }
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('PDO', $extensions) && in_array('pdo_sqlsrv', $extensions);
+ }
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ return true;
+ }
+
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ if(preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match))
+ {
+ return $match[1] == $errno;
+ }
+ return false;
+ }
+
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ public function isConnectionUTF8()
+ {
+ //check the getconnection, it's specified on the connection string.
+ return true;
+ }
+
+ /**
+ * Retrieve client version in PHP style
+ *
+ * @return string
+ */
+ public function getClientVersion()
+ {
+ $this->_connect();
+ try
+ {
+ $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
+ $requiredVersion = Zend_Registry::get('config')->General->minimum_mssql_client_version;
+ if(version_compare($version['DriverVer'], $requiredVersion) === -1)
+ {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MSSQL', $serverVersion, $requiredVersion)));
+ }
+ else
+ {
+ return $version['DriverVer'];
+ }
+ }
+ catch (PDOException $e)
+ {
+ // In case of the driver doesn't support getting attributes
+ }
+
+ return null;
+ }
+}
diff --git a/oss/piwik/trunk/core/Db/Adapter/Pdo/Mysql.php b/oss/piwik/trunk/core/Db/Adapter/Pdo/Mysql.php
new file mode 100644
index 00000000..cce07214
--- /dev/null
+++ b/oss/piwik/trunk/core/Db/Adapter/Pdo/Mysql.php
@@ -0,0 +1,161 @@
+_connection)
+ {
+ return $this->_connection;
+ }
+
+ $this->_connect();
+
+ /**
+ * Before MySQL 5.1.17, server-side prepared statements
+ * do not use the query cache.
+ * @see http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html
+ *
+ * MySQL also does not support preparing certain DDL and SHOW
+ * statements.
+ * @see http://framework.zend.com/issues/browse/ZF-1398
+ */
+ $this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
+ $this->_connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
+
+ return $this->_connection;
+ }
+
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig()
+ {
+ $this->_config = array();
+ }
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 3306;
+ }
+
+ /**
+ * Check MySQL version
+ */
+ public function checkServerVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $requiredVersion = Zend_Registry::get('config')->General->minimum_mysql_version;
+ if(version_compare($serverVersion, $requiredVersion) === -1)
+ {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
+ }
+ }
+
+ /**
+ * Check client version compatibility against database server
+ */
+ public function checkClientVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $clientVersion = $this->getClientVersion();
+ if(version_compare($serverVersion, '5') >= 0
+ && version_compare($clientVersion, '5') < 0)
+ {
+ throw new Exception(Piwik_TranslateException('General_ExceptionIncompatibleClientServerVersions', array('MySQL', $clientVersion, $serverVersion)));
+ }
+ }
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('PDO', $extensions) && in_array('pdo_mysql', $extensions) && in_array('mysql', PDO::getAvailableDrivers());
+ }
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ return true;
+ }
+
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ if(preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match))
+ {
+ return $match[1] == $errno;
+ }
+ return false;
+ }
+
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ public function isConnectionUTF8()
+ {
+ $charsetInfo = $this->fetchAll('SHOW VARIABLES LIKE ?', array('character_set_connection'));
+ $charset = $charsetInfo[0]['Value'];
+ return $charset === 'utf8';
+ }
+
+ /**
+ * Retrieve client version in PHP style
+ *
+ * @return string
+ */
+ public function getClientVersion()
+ {
+ $this->_connect();
+ try {
+ $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
+ $matches = null;
+ if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
+ return $matches[1];
+ }
+ } catch (PDOException $e) {
+ // In case of the driver doesn't support getting attributes
+ }
+ return null;
+ }
+}
diff --git a/oss/piwik/trunk/core/Db/Adapter/Pdo/Pgsql.php b/oss/piwik/trunk/core/Db/Adapter/Pdo/Pgsql.php
new file mode 100644
index 00000000..c5d4320a
--- /dev/null
+++ b/oss/piwik/trunk/core/Db/Adapter/Pdo/Pgsql.php
@@ -0,0 +1,163 @@
+_config = array();
+ }
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 5432;
+ }
+
+ /**
+ * Check PostgreSQL version
+ */
+ public function checkServerVersion()
+ {
+ $databaseVersion = $this->getServerVersion();
+ $requiredVersion = Zend_Registry::get('config')->General->minimum_pgsql_version;
+ if(version_compare($databaseVersion, $requiredVersion) === -1)
+ {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('PostgreSQL', $databaseVersion, $requiredVersion)));
+ }
+ }
+
+ /**
+ * Check client version compatibility against database server
+ */
+ public function checkClientVersion()
+ {
+ }
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('PDO', $extensions) && in_array('pdo_pgsql', $extensions);
+ }
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ // large objects must be loaded from a file using a non-SQL API
+ // and then referenced by the object ID (oid);
+ // the alternative, bytea fields, incur a space and time
+ // penalty for encoding/decoding
+ return false;
+ }
+
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ // map MySQL driver-specific error codes to PostgreSQL SQLSTATE
+ $map = array(
+ // MySQL: Unknown database '%s'
+ // PostgreSQL: database "%s" does not exist
+ '1049' => '08006',
+
+ // MySQL: Table '%s' already exists
+ // PostgreSQL: relation "%s" already exists
+ '1050' => '42P07',
+
+ // MySQL: Unknown column '%s' in '%s'
+ // PostgreSQL: column "%s" does not exist
+ '1054' => '42703',
+
+ // MySQL: Duplicate column name '%s'
+ // PostgreSQL: column "%s" of relation "%s" already exists
+ '1060' => '42701',
+
+ // MySQL: Duplicate key name '%s'
+ // PostgreSQL: relation "%s" already exists
+ '1061' => '42P07',
+
+ // MySQL: Duplicate entry '%s' for key '%s'
+ // PostgreSQL: duplicate key violates unique constraint
+ '1062' => '23505',
+
+ // MySQL: Can't DROP '%s'; check that column/key exists
+ // PostgreSQL: index "%s" does not exist
+ '1091' => '42704',
+
+ // MySQL: Table '%s.%s' doesn't exist
+ // PostgreSQL: relation "%s" does not exist
+ '1146' => '42P01',
+ );
+
+ if(preg_match('/([0-9]{2}[0-9P][0-9]{2})/', $e->getMessage(), $match))
+ {
+ return $match[1] == $map[$errno];
+ }
+ return false;
+ }
+
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ public function isConnectionUTF8()
+ {
+ $charset = $this->fetchOne('SHOW client_encoding');
+ return strtolower($charset) === 'utf8';
+ }
+
+ /**
+ * Retrieve client version in PHP style
+ *
+ * @return string
+ */
+ public function getClientVersion()
+ {
+ $this->_connect();
+ try {
+ $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
+ $matches = null;
+ if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
+ return $matches[1];
+ }
+ } catch (PDOException $e) {
+ // In case of the driver doesn't support getting attributes
+ }
+ return null;
+ }
+}
diff --git a/oss/piwik/trunk/core/Db/Schema.php b/oss/piwik/trunk/core/Db/Schema.php
new file mode 100644
index 00000000..12d7c568
--- /dev/null
+++ b/oss/piwik/trunk/core/Db/Schema.php
@@ -0,0 +1,282 @@
+ array(
+ 'Myisam',
+// 'Innodb',
+// 'Infinidb',
+ ),
+
+ // Microsoft SQL Server
+// 'MSSQL' => array( 'Mssql' ),
+
+ // PostgreSQL
+// 'PDO_PGSQL' => array( 'Pgsql' ),
+
+ // IBM DB2
+// 'IBM' => array( 'Ibm' ),
+
+ // Oracle
+// 'OCI' => array( 'Oci' ),
+ );
+
+ $adapterName = strtoupper($adapterName);
+ switch($adapterName)
+ {
+ case 'PDO_MYSQL':
+ case 'MYSQLI':
+ $adapterName = 'MYSQL';
+ break;
+
+ case 'PDO_MSSQL':
+ case 'SQLSRV':
+ $adapterName = 'MSSQL';
+ break;
+
+ case 'PDO_IBM':
+ case 'DB2':
+ $adapterName = 'IBM';
+ break;
+
+ case 'PDO_OCI':
+ case 'ORACLE':
+ $adapterName = 'OCI';
+ break;
+ }
+ $schemaNames = $allSchemaNames[$adapterName];
+
+ $schemas = array();
+
+ foreach($schemaNamess as $schemaName)
+ {
+ $className = 'Piwik_Db_Schema_'.$schemaName;
+ if(call_user_func(array($className, 'isAvailable')))
+ {
+ $schemas[] = $schemaName;
+ }
+ }
+
+ return $schemas;
+ }
+
+ /**
+ * Load schema
+ */
+ private function loadSchema()
+ {
+ $schema = null;
+ Piwik_PostEvent('Schema.loadSchema', $schema);
+ if($schema === null)
+ {
+ $config = Zend_Registry::get('config');
+ $dbInfos = $config->database->toArray();
+ if(isset($dbInfos['schema']))
+ {
+ $schemaName = $dbInfos['schema'];
+ }
+ else
+ {
+ $schemaName = 'Myisam';
+ }
+ $className = self::getSchemaClassName($schemaName);
+ $schema = new $className();
+ }
+ $this->schema = $schema;
+ }
+
+ /**
+ * Returns an instance that subclasses Piwik_Db_Schema
+ *
+ * @return Piwik_Db_Schema_Interface
+ */
+ private function getSchema()
+ {
+ if ($this->schema === null)
+ {
+ $this->loadSchema();
+ }
+ return $this->schema;
+ }
+
+ /**
+ * Get the SQL to create a specific Piwik table
+ *
+ * @return string SQL
+ */
+ public function getTableCreateSql( $tableName )
+ {
+ return $this->getSchema()->getTableCreateSql($tableName);
+ }
+
+ /**
+ * Get the SQL to create Piwik tables
+ *
+ * @return array of strings containing SQL
+ */
+ public function getTablesCreateSql()
+ {
+ return $this->getSchema()->getTablesCreateSql();
+ }
+
+ /**
+ * Create database
+ */
+ public function createDatabase( $dbName = null )
+ {
+ $this->getSchema()->createDatabase($dbName);
+ }
+
+ /**
+ * Drop database
+ */
+ public function dropDatabase()
+ {
+ $this->getSchema()->dropDatabase();
+ }
+
+ /**
+ * Create all tables
+ */
+ public function createTables()
+ {
+ $this->getSchema()->createTables();
+ }
+
+ /**
+ * Creates an entry in the User table for the "anonymous" user.
+ */
+ public function createAnonymousUser()
+ {
+ $this->getSchema()->createAnonymousUser();
+ }
+
+ /**
+ * Truncate all tables
+ */
+ public function truncateAllTables()
+ {
+ $this->getSchema()->truncateAllTables();
+ }
+
+ /**
+ * Drop specific tables
+ */
+ public function dropTables( $doNotDelete = array() )
+ {
+ $this->getSchema()->dropTables($doNotDelete);
+ }
+
+ /**
+ * Names of all the prefixed tables in piwik
+ * Doesn't use the DB
+ *
+ * @return array Table names
+ */
+ public function getTablesNames()
+ {
+ return $this->getSchema()->getTablesNames();
+ }
+
+ /**
+ * Get list of tables installed
+ *
+ * @param bool $forceReload Invalidate cache
+ * @param string $idSite
+ * @return array Tables installed
+ */
+ public function getTablesInstalled($forceReload = true, $idSite = null)
+ {
+ return $this->getSchema()->getTablesInstalled($forceReload, $idSite);
+ }
+
+ /**
+ * Returns true if Piwik tables exist
+ *
+ * @return bool True if tables exist; false otherwise
+ */
+ public function hasTables()
+ {
+ return $this->getSchema()->hasTables();
+ }
+}
+
+/**
+ * @package Piwik
+ */
+interface Piwik_Db_Schema_Interface
+{
+ static public function isAvailable();
+
+ public function getTableCreateSql($tableName);
+ public function getTablesCreateSql();
+
+ public function createDatabase( $dbName = null );
+ public function dropDatabase();
+
+ public function createTables();
+ public function createAnonymousUser();
+ public function truncateAllTables();
+ public function dropTables( $doNotDelete = array() );
+
+ public function getTablesNames();
+ public function getTablesInstalled($forceReload = true, $idSite = null);
+ public function hasTables();
+}
diff --git a/oss/piwik/trunk/core/Db/Schema/Myisam.php b/oss/piwik/trunk/core/Db/Schema/Myisam.php
new file mode 100644
index 00000000..d68b2340
--- /dev/null
+++ b/oss/piwik/trunk/core/Db/Schema/Myisam.php
@@ -0,0 +1,484 @@
+fetchAssoc('SHOW ENGINES');
+ if(key_exists($engineName, $allEngines))
+ {
+ $support = $allEngines[$engineName]['Support'];
+ return $support == 'DEFAULT' || $support == 'YES';
+ }
+ return false;
+ }
+
+ /**
+ * Is this schema available?
+ *
+ * @return bool True if schema is available; false otherwise
+ */
+ static public function isAvailable()
+ {
+ return self::hasStorageEngine('MyISAM');
+ }
+
+ /**
+ * Get the SQL to create Piwik tables
+ *
+ * @return array of strings containing SQL
+ */
+ public function getTablesCreateSql()
+ {
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+ $tables = array(
+ 'user' => "CREATE TABLE {$prefixTables}user (
+ login VARCHAR(100) NOT NULL,
+ password CHAR(32) NOT NULL,
+ alias VARCHAR(45) NOT NULL,
+ email VARCHAR(100) NOT NULL,
+ token_auth CHAR(32) NOT NULL,
+ date_registered TIMESTAMP NULL,
+ PRIMARY KEY(login),
+ UNIQUE KEY uniq_keytoken(token_auth)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'access' => "CREATE TABLE {$prefixTables}access (
+ login VARCHAR(100) NOT NULL,
+ idsite INTEGER UNSIGNED NOT NULL,
+ access VARCHAR(10) NULL,
+ PRIMARY KEY(login, idsite)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'site' => "CREATE TABLE {$prefixTables}site (
+ idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ name VARCHAR(90) NOT NULL,
+ main_url VARCHAR(255) NOT NULL,
+ ts_created TIMESTAMP NULL,
+ timezone VARCHAR( 50 ) NOT NULL,
+ currency CHAR( 3 ) NOT NULL,
+ excluded_ips TEXT NOT NULL,
+ excluded_parameters VARCHAR ( 255 ) NOT NULL,
+ PRIMARY KEY(idsite)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'site_url' => "CREATE TABLE {$prefixTables}site_url (
+ idsite INTEGER(10) UNSIGNED NOT NULL,
+ url VARCHAR(255) NOT NULL,
+ PRIMARY KEY(idsite, url)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'goal' => " CREATE TABLE `{$prefixTables}goal` (
+ `idsite` int(11) NOT NULL,
+ `idgoal` int(11) NOT NULL,
+ `name` varchar(50) NOT NULL,
+ `match_attribute` varchar(20) NOT NULL,
+ `pattern` varchar(255) NOT NULL,
+ `pattern_type` varchar(10) NOT NULL,
+ `case_sensitive` tinyint(4) NOT NULL,
+ `revenue` float NOT NULL,
+ `deleted` tinyint(4) NOT NULL default '0',
+ PRIMARY KEY (`idsite`,`idgoal`)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'logger_message' => "CREATE TABLE {$prefixTables}logger_message (
+ idlogger_message INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ timestamp TIMESTAMP NULL,
+ message TEXT NULL,
+ PRIMARY KEY(idlogger_message)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'logger_api_call' => "CREATE TABLE {$prefixTables}logger_api_call (
+ idlogger_api_call INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ class_name VARCHAR(255) NULL,
+ method_name VARCHAR(255) NULL,
+ parameter_names_default_values TEXT NULL,
+ parameter_values TEXT NULL,
+ execution_time FLOAT NULL,
+ caller_ip INT UNSIGNED NULL,
+ timestamp TIMESTAMP NULL,
+ returned_value TEXT NULL,
+ PRIMARY KEY(idlogger_api_call)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'logger_error' => "CREATE TABLE {$prefixTables}logger_error (
+ idlogger_error INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ timestamp TIMESTAMP NULL,
+ message TEXT NULL,
+ errno INTEGER UNSIGNED NULL,
+ errline INTEGER UNSIGNED NULL,
+ errfile VARCHAR(255) NULL,
+ backtrace TEXT NULL,
+ PRIMARY KEY(idlogger_error)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'logger_exception' => "CREATE TABLE {$prefixTables}logger_exception (
+ idlogger_exception INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ timestamp TIMESTAMP NULL,
+ message TEXT NULL,
+ errno INTEGER UNSIGNED NULL,
+ errline INTEGER UNSIGNED NULL,
+ errfile VARCHAR(255) NULL,
+ backtrace TEXT NULL,
+ PRIMARY KEY(idlogger_exception)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'log_action' => "CREATE TABLE {$prefixTables}log_action (
+ idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ name TEXT,
+ hash INTEGER(10) UNSIGNED NOT NULL,
+ type TINYINT UNSIGNED NULL,
+ PRIMARY KEY(idaction),
+ INDEX index_type_hash (type, hash)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'log_visit' => "CREATE TABLE {$prefixTables}log_visit (
+ idvisit INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ idsite INTEGER(10) UNSIGNED NOT NULL,
+ visitor_localtime TIME NOT NULL,
+ visitor_idcookie CHAR(32) NOT NULL,
+ visitor_returning TINYINT(1) NOT NULL,
+ visit_first_action_time DATETIME NOT NULL,
+ visit_last_action_time DATETIME NOT NULL,
+ visit_server_date DATE NOT NULL,
+ visit_exit_idaction_url INTEGER(11) NOT NULL,
+ visit_entry_idaction_url INTEGER(11) NOT NULL,
+ visit_total_actions SMALLINT(5) UNSIGNED NOT NULL,
+ visit_total_time SMALLINT(5) UNSIGNED NOT NULL,
+ visit_goal_converted TINYINT(1) NOT NULL,
+ referer_type INTEGER UNSIGNED NULL,
+ referer_name VARCHAR(70) NULL,
+ referer_url TEXT NOT NULL,
+ referer_keyword VARCHAR(255) NULL,
+ config_md5config CHAR(32) NOT NULL,
+ config_os CHAR(3) NOT NULL,
+ config_browser_name VARCHAR(10) NOT NULL,
+ config_browser_version VARCHAR(20) NOT NULL,
+ config_resolution VARCHAR(9) NOT NULL,
+ config_pdf TINYINT(1) NOT NULL,
+ config_flash TINYINT(1) NOT NULL,
+ config_java TINYINT(1) NOT NULL,
+ config_director TINYINT(1) NOT NULL,
+ config_quicktime TINYINT(1) NOT NULL,
+ config_realplayer TINYINT(1) NOT NULL,
+ config_windowsmedia TINYINT(1) NOT NULL,
+ config_gears TINYINT(1) NOT NULL,
+ config_silverlight TINYINT(1) NOT NULL,
+ config_cookie TINYINT(1) NOT NULL,
+ location_ip INT UNSIGNED NOT NULL,
+ location_browser_lang VARCHAR(20) NOT NULL,
+ location_country CHAR(3) NOT NULL,
+ location_continent CHAR(3) NOT NULL,
+ PRIMARY KEY(idvisit),
+ INDEX index_idsite_idvisit (idsite, idvisit),
+ INDEX index_idsite_date_config (idsite, visit_server_date, config_md5config(8)),
+ INDEX index_idsite_datetime_config (idsite, visit_last_action_time, config_md5config(8))
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` (
+ idvisit int(10) unsigned NOT NULL,
+ idsite int(10) unsigned NOT NULL,
+ visitor_idcookie char(32) NOT NULL,
+ server_time datetime NOT NULL,
+ idaction_url int(11) default NULL,
+ idlink_va int(11) default NULL,
+ referer_idvisit int(10) unsigned default NULL,
+ referer_visit_server_date date default NULL,
+ referer_type int(10) unsigned default NULL,
+ referer_name varchar(70) default NULL,
+ referer_keyword varchar(255) default NULL,
+ visitor_returning tinyint(1) NOT NULL,
+ location_country char(3) NOT NULL,
+ location_continent char(3) NOT NULL,
+ url text NOT NULL,
+ idgoal int(10) unsigned NOT NULL,
+ revenue float default NULL,
+ PRIMARY KEY (idvisit, idgoal),
+ INDEX index_idsite_datetime ( idsite, server_time )
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action (
+ idlink_va INTEGER(11) NOT NULL AUTO_INCREMENT,
+ idvisit INTEGER(10) UNSIGNED NOT NULL,
+ idaction_url INTEGER(10) UNSIGNED NOT NULL,
+ idaction_url_ref INTEGER(10) UNSIGNED NOT NULL,
+ idaction_name INTEGER(10) UNSIGNED,
+ time_spent_ref_action INTEGER(10) UNSIGNED NOT NULL,
+ PRIMARY KEY(idlink_va),
+ INDEX index_idvisit(idvisit)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling (
+ query TEXT NOT NULL,
+ count INTEGER UNSIGNED NULL,
+ sum_time_ms FLOAT NULL,
+ UNIQUE KEY query(query(100))
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'option' => "CREATE TABLE `{$prefixTables}option` (
+ option_name VARCHAR( 64 ) NOT NULL,
+ option_value LONGTEXT NOT NULL,
+ autoload TINYINT NOT NULL DEFAULT '1',
+ PRIMARY KEY ( option_name )
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric (
+ idarchive INTEGER UNSIGNED NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ idsite INTEGER UNSIGNED NULL,
+ date1 DATE NULL,
+ date2 DATE NULL,
+ period TINYINT UNSIGNED NULL,
+ ts_archived DATETIME NULL,
+ value FLOAT NULL,
+ PRIMARY KEY(idarchive, name),
+ INDEX index_idsite_dates_period(idsite, date1, date2, period, ts_archived),
+ INDEX index_period_archived(period, ts_archived)
+ ) DEFAULT CHARSET=utf8
+ ",
+
+ 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob (
+ idarchive INTEGER UNSIGNED NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ idsite INTEGER UNSIGNED NULL,
+ date1 DATE NULL,
+ date2 DATE NULL,
+ period TINYINT UNSIGNED NULL,
+ ts_archived DATETIME NULL,
+ value MEDIUMBLOB NULL,
+ PRIMARY KEY(idarchive, name),
+ INDEX index_period_archived(period, ts_archived)
+ ) DEFAULT CHARSET=utf8
+ ",
+ );
+ return $tables;
+ }
+
+ /**
+ * Get the SQL to create a specific Piwik table
+ *
+ * @param string $tableName
+ * @return string SQL
+ */
+ public function getTableCreateSql( $tableName )
+ {
+ $tables = Piwik::getTablesCreateSql();
+
+ if(!isset($tables[$tableName]))
+ {
+ throw new Exception("The table '$tableName' SQL creation code couldn't be found.");
+ }
+
+ return $tables[$tableName];
+ }
+
+ /**
+ * Names of all the prefixed tables in piwik
+ * Doesn't use the DB
+ *
+ * @return array Table names
+ */
+ public function getTablesNames()
+ {
+ $aTables = array_keys($this->getTablesCreateSql());
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+ $return = array();
+ foreach($aTables as $table)
+ {
+ $return[] = $prefixTables.$table;
+ }
+ return $return;
+ }
+
+ private $tablesInstalled = null;
+
+ /**
+ * Get list of tables installed
+ *
+ * @param bool $forceReload Invalidate cache
+ * @param string $idSite
+ * @return array Tables installed
+ */
+ public function getTablesInstalled($forceReload = true, $idSite = null)
+ {
+ if(is_null($this->tablesInstalled)
+ || $forceReload === true)
+ {
+ $db = Zend_Registry::get('db');
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+
+ $allTables = $db->fetchCol("SHOW TABLES");
+
+ // all the tables to be installed
+ $allMyTables = $this->getTablesNames();
+
+ // we get the intersection between all the tables in the DB and the tables to be installed
+ $tablesInstalled = array_intersect($allMyTables, $allTables);
+
+ // at this point we have only the piwik tables which is good
+ // but we still miss the piwik generated tables (using the class Piwik_TablePartitioning)
+ $idSiteInSql = "no";
+ if(!is_null($idSite))
+ {
+ $idSiteInSql = $idSite;
+ }
+ $allArchiveNumeric = $db->fetchCol("/* SHARDING_ID_SITE = ".$idSiteInSql." */
+ SHOW TABLES LIKE '".$prefixTables."archive_numeric%'");
+ $allArchiveBlob = $db->fetchCol("/* SHARDING_ID_SITE = ".$idSiteInSql." */
+ SHOW TABLES LIKE '".$prefixTables."archive_blob%'");
+
+ $allTablesReallyInstalled = array_merge($tablesInstalled, $allArchiveNumeric, $allArchiveBlob);
+
+ $this->tablesInstalled = $allTablesReallyInstalled;
+ }
+ return $this->tablesInstalled;
+ }
+
+ /**
+ * Do tables exist?
+ *
+ * @return bool True if tables exist; false otherwise
+ */
+ public function hasTables()
+ {
+ return count($this->getTablesInstalled()) != 0;
+ }
+
+ /**
+ * Create database
+ *
+ * @param string $dbName
+ */
+ public function createDatabase( $dbName = null )
+ {
+ if(is_null($dbName))
+ {
+ $dbName = Zend_Registry::get('config')->database->dbname;
+ }
+ Piwik_Exec("CREATE DATABASE IF NOT EXISTS ".$dbName);
+ }
+
+ /**
+ * Drop database
+ */
+ public function dropDatabase()
+ {
+ $dbName = Zend_Registry::get('config')->database->dbname;
+ Piwik_Exec("DROP DATABASE IF EXISTS " . $dbName);
+
+ }
+
+ /**
+ * Create all tables
+ */
+ public function createTables()
+ {
+ $db = Zend_Registry::get('db');
+ $config = Zend_Registry::get('config');
+ $prefixTables = $config->database->tables_prefix;
+
+ $tablesAlreadyInstalled = $this->getTablesInstalled();
+ $tablesToCreate = $this->getTablesCreateSql();
+ unset($tablesToCreate['archive_blob']);
+ unset($tablesToCreate['archive_numeric']);
+
+ foreach($tablesToCreate as $tableName => $tableSql)
+ {
+ $tableName = $prefixTables . $tableName;
+ if(!in_array($tableName, $tablesAlreadyInstalled))
+ {
+ $db->query( $tableSql );
+ }
+ }
+ }
+
+ /**
+ * Creates an entry in the User table for the "anonymous" user.
+ */
+ public function createAnonymousUser()
+ {
+ // The anonymous user is the user that is assigned by default
+ // note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin
+ $db = Zend_Registry::get('db');
+ $db->query("INSERT INTO ". Piwik_Common::prefixTable("user") . "
+ VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', '".Piwik_Date::factory('now')->getDatetime()."' );" );
+ }
+
+ /**
+ * Truncate all tables
+ */
+ public function truncateAllTables()
+ {
+ $tablesAlreadyInstalled = $this->getTablesInstalled($forceReload = true);
+ foreach($tablesAlreadyInstalled as $table)
+ {
+ Piwik_Query("TRUNCATE `$table`");
+ }
+ }
+
+ /**
+ * Drop specific tables
+ *
+ * @param array $doNotDelete Names of tables to not delete
+ */
+ public function dropTables( $doNotDelete = array() )
+ {
+ $tablesAlreadyInstalled = $this->getTablesInstalled();
+ $db = Zend_Registry::get('db');
+
+ $doNotDeletePattern = '/('.implode('|',$doNotDelete).')/';
+
+ foreach($tablesAlreadyInstalled as $tableName)
+ {
+ if( count($doNotDelete) == 0
+ || (!in_array($tableName,$doNotDelete)
+ && !preg_match($doNotDeletePattern,$tableName)
+ )
+ )
+ {
+ $db->query("DROP TABLE `$tableName`");
+ }
+ }
+ }
+}
diff --git a/oss/piwik/trunk/core/Log/APICall.php b/oss/piwik/trunk/core/Log/APICall.php
new file mode 100644
index 00000000..178f2378
--- /dev/null
+++ b/oss/piwik/trunk/core/Log/APICall.php
@@ -0,0 +1,117 @@
+setEventItem('caller_ip', Piwik_Common::getIp() );
+ }
+
+ public function logEvent($className, $methodName, $parameterNames, $parameterValues, $executionTime, $returnedValue)
+ {
+ $event = array();
+ $event['class_name'] = $className;
+ $event['method_name'] = $methodName;
+ $event['parameter_names_default_values'] = serialize($parameterNames);
+ $event['parameter_values'] = serialize($parameterValues);
+ $event['execution_time'] = $executionTime;
+ $event['returned_value'] = is_array($returnedValue) ? serialize($returnedValue) : $returnedValue;
+
+ parent::log($event, Piwik_Log::INFO, null);
+ }
+}
+
+/**
+ * Class used to format the API Call log on the screen.
+ *
+ * @package Piwik
+ * @subpackage Piwik_Log
+ */
+class Piwik_Log_APICall_Formatter_ScreenFormatter extends Piwik_Log_Formatter_ScreenFormatter
+{
+ /**
+ * Formats data into a single line to be written by the writer.
+ *
+ * @param array $event event data
+ * @return string formatted line to write to the log
+ */
+ public function format($event)
+ {
+ $str = "\n
";
+ $str .= "Called: {$event['class_name']}.{$event['method_name']} (took {$event['execution_time']}ms)\n
";
+ $str .= "Parameters: ";
+ $parameterNamesAndDefault = unserialize($event['parameter_names_default_values']);
+ $parameterValues = unserialize($event['parameter_values']);
+
+ $i = 0;
+ foreach($parameterNamesAndDefault as $pName => $pDefault)
+ {
+ if(isset($parameterValues[$i]))
+ {
+ $currentValue = $parameterValues[$i];
+ }
+ else
+ {
+ $currentValue = $pDefault;
+ }
+
+ $currentValue = $this->formatValue($currentValue);
+ $str .= "$pName = $currentValue, ";
+
+ $i++;
+ }
+ $str .= "\n
";
+
+// $str .= "Returned: ".$this->formatValue($event['returned_value']);
+ $str .= "\n
";
+ return parent::format($str);
+ }
+
+ private function formatValue( $value )
+ {
+ if(is_string($value))
+ {
+ $value = "'$value'";
+ }
+ if(is_null($value))
+ {
+ $value= 'null';
+ }
+ if(is_array($value))
+ {
+ $value = "array( ".implode(", ", $value). ")";
+ }
+ return $value;
+
+ }
+}
diff --git a/oss/piwik/trunk/core/Log/Error.php b/oss/piwik/trunk/core/Log/Error.php
new file mode 100644
index 00000000..912430de
--- /dev/null
+++ b/oss/piwik/trunk/core/Log/Error.php
@@ -0,0 +1,116 @@
+setFormatter( $this->screenFormatter );
+ $this->addWriter($writerScreen);
+ }
+
+ public function logEvent($errno, $errstr, $errfile, $errline, $backtrace)
+ {
+ $event = array();
+ $event['errno'] = $errno;
+ $event['message'] = $errstr;
+ $event['errfile'] = $errfile;
+ $event['errline'] = $errline;
+ $event['backtrace'] = $backtrace;
+
+ parent::log($event, Piwik_Log::ERR, null);
+ }
+}
+
+/**
+ * Format an error event to be displayed on the screen.
+ *
+ * @package Piwik
+ * @subpackage Piwik_Log
+ */
+class Piwik_Log_Error_Formatter_ScreenFormatter extends Piwik_Log_Formatter_ScreenFormatter
+{
+ /**
+ * Formats data into a single line to be written by the writer.
+ *
+ * @param array $event event data
+ * @return string formatted line to write to the log
+ */
+ public function format($event)
+ {
+ $event = parent::formatEvent($event);
+
+ $errno = $event['errno'] ;
+ $errstr = $event['message'] ;
+ $errfile = $event['errfile'] ;
+ $errline = $event['errline'] ;
+ $backtrace = $event['backtrace'] ;
+
+ $strReturned = '';
+ $errno = $errno & error_reporting();
+
+ // problem when using error_reporting with the @ silent fail operator
+ // it gives an errno 0, and in this case the objective is to NOT display anything on the screen!
+ // is there any other case where the errno is zero at this point?
+ if($errno == 0) return '';
+ $strReturned .= "\n