Composer Compliant File Names
This commit is contained in:
753
inc/Db.php
Normal file
753
inc/Db.php
Normal file
@@ -0,0 +1,753 @@
|
||||
<?php
|
||||
|
||||
namespace Franzz\Objects;
|
||||
use \Settings;
|
||||
|
||||
/**
|
||||
* MySql query manager and generator
|
||||
* @author franzz
|
||||
* @version 2.1
|
||||
*/
|
||||
class Db extends PhpObject
|
||||
{
|
||||
const DB_PEACHY = 'NO_ERR';
|
||||
const DB_NO_CONN = 'ERR_1';
|
||||
const DB_NO_DATA = 'ERR_2';
|
||||
const DB_NO_TABLE = 'ERR_3';
|
||||
const ID_TAG = 'id_';
|
||||
|
||||
//Database formats
|
||||
const MONTH_FORMAT = 'Ym';
|
||||
const DATE_FORMAT = 'Y-m-d';
|
||||
const TIMESTAMP_FORMAT = 'Y-m-d H:i:s';
|
||||
|
||||
public $sDbState;
|
||||
private $bTrace;
|
||||
|
||||
/**
|
||||
* SQL connection Handle
|
||||
* @var \mysqli
|
||||
*/
|
||||
private $oConnection;
|
||||
private $asConf;
|
||||
private $asOptions;
|
||||
|
||||
/**
|
||||
* Tables & fields descriptions
|
||||
* array( 'tables'=>array('table_name1'=>array('table_field1', 'table_field2', ...), 'table_name2'=>array(...)),
|
||||
* 'types'=>array('field1'=>'field_type1', 'field2'=>'field_type2', ...)
|
||||
* 'constraints'=>array('table_name1'=>'table_contraint1', 'table_name2'=>'table_contraint2', ...),
|
||||
* 'cascading_delete'=>array('table_name1'=>array('linked_table1', 'linked_table2', ...), 'table_name2'=>...))
|
||||
* @var Array
|
||||
*/
|
||||
public function __construct($asConf, $asOptions)
|
||||
{
|
||||
$this->asConf = $asConf;
|
||||
$this->asOptions = $asOptions;
|
||||
|
||||
parent::__construct(__FILE__, Settings::DEBUG);
|
||||
$this->oConnection = new \mysqli($this->getConf('server'), $this->getConf('user'), $this->getConf('pass'));
|
||||
$this->syncPhpParams($this->getConf('encoding'));
|
||||
|
||||
$this->setTrace(false);
|
||||
if($this->oConnection->connect_error)
|
||||
{
|
||||
$this->addError('bug connection : '.$this->oConnection->connect_error);
|
||||
$this->sDbState = self::DB_NO_CONN;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!$this->oConnection->select_db($this->getConf('database')))
|
||||
{
|
||||
$this->addError('Could not find database "'.$this->sDatabase.'"');
|
||||
$this->sDbState = self::DB_NO_DATA;
|
||||
}
|
||||
elseif(empty($this->getArrayQuery("SHOW TABLES")))
|
||||
{
|
||||
$this->sDbState = self::DB_NO_TABLE;
|
||||
}
|
||||
else $this->sDbState = self::DB_PEACHY;
|
||||
}
|
||||
}
|
||||
|
||||
private function getConf($sConf) {
|
||||
return $this->asConf[$sConf] ?? null;
|
||||
}
|
||||
|
||||
private function syncPhpParams($sEncoding)
|
||||
{
|
||||
//Characters encoding
|
||||
$this->oConnection->set_charset($sEncoding); //SET NAMES
|
||||
|
||||
//Timezone
|
||||
$this->setQuery("SET time_zone='".date_default_timezone_get()."'");
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
parent::__destruct();
|
||||
$this->oConnection->close();
|
||||
}
|
||||
|
||||
public function setTrace($bTrace=true)
|
||||
{
|
||||
$this->bTrace = $bTrace;
|
||||
}
|
||||
|
||||
public function getTrace()
|
||||
{
|
||||
return $this->bTrace;
|
||||
}
|
||||
|
||||
public function getTables()
|
||||
{
|
||||
return array_keys($this->asOptions['tables']);
|
||||
}
|
||||
|
||||
public function install()
|
||||
{
|
||||
//Create Database
|
||||
$this->setQuery("DROP DATABASE IF EXISTS ".$this->sDatabase);
|
||||
$this->setQuery("CREATE DATABASE ".$this->sDatabase." DEFAULT CHARACTER SET ".$this->getConf('encoding')." DEFAULT COLLATE ".$this->getConf('encoding')."_general_ci");
|
||||
$this->oConnection->select_db($this->sDatabase);
|
||||
|
||||
//Create tables
|
||||
@array_walk($this->getInstallQueries(), array($this, 'setQuery'));
|
||||
}
|
||||
|
||||
public function getBackup() {
|
||||
$sBackupFile = uniqid('backup_').'.sql';
|
||||
|
||||
$sAppPath = '';
|
||||
if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') $sAppPath = 'C:\ProgramData\xampp\mysql\bin\\';
|
||||
exec($sAppPath.'mysqldump --user='.$this->getConf('user').' --password='.$this->getConf('pass').' '.$this->getConf('database').' --add-drop-table --result-file='.$sBackupFile);
|
||||
if(file_exists($sBackupFile)) {
|
||||
$sBackup = file_get_contents($sBackupFile);
|
||||
unlink($sBackupFile);
|
||||
return $sBackup;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public function restoreBackup($sBackupFile) {
|
||||
$sAppPath = '';
|
||||
if(file_exists($sBackupFile)) {
|
||||
if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') $sAppPath = 'C:\ProgramData\xampp\mysql\bin\\';
|
||||
return exec($sAppPath.'mysql --user='.$this->getConf('user').' --password='.$this->getConf('pass').' '.$this->getConf('database').' < '.$sBackupFile);
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public function loadFile($sFilePath) {
|
||||
set_time_limit(0);
|
||||
$bResult = false;
|
||||
|
||||
if(file_exists($sFilePath))
|
||||
{
|
||||
$sContent = file_get_contents($sFilePath);
|
||||
$sContent = ToolBox::fixEOL($sContent);
|
||||
if(str_replace(ToolBox::FILE_EOL, '', $sContent)!='')
|
||||
{
|
||||
$asLines = explode(ToolBox::FILE_EOL, $sContent);
|
||||
$sSql = '';
|
||||
foreach ($asLines as $sLine)
|
||||
{
|
||||
$sSql .= trim($sLine);
|
||||
if(substr($sSql, -1)==';') //Multi line SQL
|
||||
{
|
||||
$asResult = $this->setQuery($sSql);
|
||||
if($asResult === false)
|
||||
{
|
||||
$this->addError('SQL failed with error: '.$this->db->error, $sSql);
|
||||
$bResult = false;
|
||||
break;
|
||||
}
|
||||
|
||||
$bResult = true;
|
||||
$sSql = '';
|
||||
}
|
||||
else $sSql .= " ";
|
||||
}
|
||||
}
|
||||
else $this->addError('File is empty: '.basename($sFilePath));
|
||||
}
|
||||
else $this->addError('File not found: '.$sFilePath);
|
||||
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
//For debug purposes
|
||||
public function getFullInstallQuery()
|
||||
{
|
||||
$asInstallQueries = $this->getInstallQueries();
|
||||
return str_replace("\n", "<br />", implode(";\n\n", $asInstallQueries))."\n\n<!-- \n".implode(";\n\n", $asInstallQueries)."\n -->";
|
||||
}
|
||||
|
||||
private function getInstallQueries()
|
||||
{
|
||||
$asTables = $this->getTables();
|
||||
$asInstallQueries = array_map(array($this, 'getInstallQuery'), $asTables);
|
||||
$asAlterQueries = $this->getForeignKeyQueries($asTables);
|
||||
return array_merge($asInstallQueries, $asAlterQueries);
|
||||
}
|
||||
|
||||
private function getInstallQuery($sTableName)
|
||||
{
|
||||
$asTableColumns = $this->getTableColumns($sTableName);
|
||||
$sQuery = "\n".$this->implodeAll($asTableColumns, "` ", "\n", "`", ",")."\n".implode(", \n", $this->getTableConstraints($sTableName));
|
||||
return "CREATE TABLE `{$sTableName}` ({$sQuery})";
|
||||
}
|
||||
|
||||
private function getForeignKeyQueries($asTableNames)
|
||||
{
|
||||
$asForeignKeyQueries = array();
|
||||
foreach($asTableNames as $sTableName)
|
||||
{
|
||||
$asTableColumns = $this->getTablecolumns($sTableName, false);
|
||||
foreach($asTableColumns as $sColumnName)
|
||||
{
|
||||
if($this->isId($sColumnName) && $sColumnName!=self::getId($sTableName))
|
||||
{
|
||||
$asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD INDEX(`".$sColumnName."`)";
|
||||
$asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD FOREIGN KEY (`".$sColumnName."`) REFERENCES ".self::getTable($sColumnName)."(`".$sColumnName."`)";
|
||||
}
|
||||
}
|
||||
}
|
||||
return $asForeignKeyQueries;
|
||||
}
|
||||
|
||||
private function setQuery($sQuery, $sTypeQuery=__FUNCTION__)
|
||||
{
|
||||
$oResult = $this->getQuery($sQuery, $sTypeQuery);
|
||||
return ($oResult!==false);
|
||||
}
|
||||
|
||||
private function getQuery($sQuery, $sTypeQuery=__FUNCTION__)
|
||||
{
|
||||
$sQuery = str_replace(array("\n", "\t"), array(" ", ""), $sQuery);
|
||||
|
||||
if($this->getTrace()) $this->addNotice($sQuery.";");
|
||||
|
||||
if(!($oResult = $this->oConnection->query($sQuery)))
|
||||
{
|
||||
$this->addError("\nErreur SQL : \n".str_replace("\t", "", $sQuery.";")."\n\n".str_replace(array("\t", "\n"), "", $this->getLastError()));
|
||||
}
|
||||
return $oResult;
|
||||
}
|
||||
|
||||
public function getLastError()
|
||||
{
|
||||
return $this->oConnection->error;
|
||||
}
|
||||
|
||||
public function getArrayQuery($sQuery, $bStringOnly=false, $sGroupBy='', $sTypeQuery=__FUNCTION__)
|
||||
{
|
||||
$iIndex = 0;
|
||||
$iColumnCount = 0;
|
||||
$asResult = array();
|
||||
$oResult = $this->getQuery($sQuery, true, $sTypeQuery);
|
||||
if($oResult!==false)
|
||||
{
|
||||
while($asCurrentRow = $oResult->fetch_array())
|
||||
{
|
||||
if($bStringOnly) $asCurrentRow = $this->arrayKeyFilter($asCurrentRow, 'is_string');
|
||||
|
||||
//Add table reel keys
|
||||
if($sGroupBy!='' && array_key_exists($sGroupBy, $asCurrentRow))
|
||||
{
|
||||
$iRowKey = $asCurrentRow[$sGroupBy];
|
||||
unset($asCurrentRow[$sGroupBy]);
|
||||
}
|
||||
else $iRowKey = $iIndex;
|
||||
|
||||
//For first loop, check table width
|
||||
if($iIndex==0) $iColumnCount = count($asCurrentRow);
|
||||
|
||||
//One column case : collapse a level
|
||||
if($iColumnCount==1) $asCurrentRow = array_shift($asCurrentRow);
|
||||
|
||||
$asResult[$iRowKey] = $asCurrentRow;
|
||||
$iIndex++;
|
||||
}
|
||||
}
|
||||
return $asResult;
|
||||
}
|
||||
|
||||
private function getMaxIncrementedValue($sTable)
|
||||
{
|
||||
return $this->selectValue($sTable, "MAX(".$this->getId($sTable).")");
|
||||
}
|
||||
|
||||
public static function getId($sTableName, $bFull=false)
|
||||
{
|
||||
$sColumnName = self::ID_TAG.self::getText($sTableName);
|
||||
return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName;
|
||||
}
|
||||
|
||||
public static function getText($sTableName, $bFull=false)
|
||||
{
|
||||
$sColumnName = mb_substr(str_replace('`', '', $sTableName), 0, -1);
|
||||
$sColumnName = mb_substr($sColumnName, -2)=='ie'?mb_substr($sColumnName, 0, -2).'y':$sColumnName;
|
||||
return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName;
|
||||
}
|
||||
|
||||
public static function getFullColumnName($sTableName, $sColumnName)
|
||||
{
|
||||
return $sTableName.".".$sColumnName;
|
||||
}
|
||||
|
||||
private function isId($sColumnName, $sTableName='')
|
||||
{
|
||||
$asTables = ($sTableName=='')?$this->getTables():array($sTableName);
|
||||
$asTableIds = array_map(array('self', 'getId'), $asTables);
|
||||
return in_array($sColumnName, $asTableIds);
|
||||
}
|
||||
|
||||
private function isField($sTableFieldName)
|
||||
{
|
||||
$asPath = explode('.', str_replace('`', '', $sTableFieldName));
|
||||
return (
|
||||
is_array($asPath)
|
||||
&& count($asPath)==2
|
||||
&& $this->isColumnInTable($asPath[0], $asPath[1])
|
||||
);
|
||||
}
|
||||
|
||||
private function getTable($sTableId)
|
||||
{
|
||||
$asTables = $this->getTables();
|
||||
$asTableIds = array_map(array('self', 'getId'), $asTables);
|
||||
if(in_array($sTableId, $asTableIds)) return $asTables[array_search($sTableId, $asTableIds)];
|
||||
else
|
||||
{
|
||||
$this->addError('Id '.$sTableId.' présent dans aucune table');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getTablecolumns($sTableName, $bTypes=true)
|
||||
{
|
||||
if(!array_key_exists($sTableName, $this->asOptions['tables'])) return false;
|
||||
|
||||
$asTableColumns = array(self::getId($sTableName));
|
||||
foreach($this->asOptions['tables'][$sTableName] as $sFieldName) $asTableColumns[] = $sFieldName;
|
||||
$asTableColumns[] = 'led';
|
||||
|
||||
if(!$bTypes) return $asTableColumns;
|
||||
|
||||
$asTableName = array_fill(0, count($asTableColumns), $sTableName);
|
||||
return array_combine($asTableColumns, array_map(array('self', 'getColumnType'), $asTableColumns, $asTableName));
|
||||
}
|
||||
|
||||
public function isColumnInTable($sTableName, $sColName) {
|
||||
$asCols = $this->getTablecolumns($sTableName, false);
|
||||
return ($asCols && in_array($sColName, $asCols));
|
||||
}
|
||||
|
||||
private function getColumnType($sColumnName, $sTableName)
|
||||
{
|
||||
$sColumnType = '';
|
||||
switch($sColumnName)
|
||||
{
|
||||
case array_key_exists($sColumnName, $this->asOptions['types']):
|
||||
$sColumnType = $this->asOptions['types'][$sColumnName];
|
||||
break;
|
||||
case 'led':
|
||||
$sColumnType = "TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP";
|
||||
break;
|
||||
case $this->isId($sColumnName, $sTableName):
|
||||
$sColumnType = "int(10) UNSIGNED auto_increment";
|
||||
break;
|
||||
case $this->isId($sColumnName):
|
||||
$sColumnType = "int(10) UNSIGNED";
|
||||
break;
|
||||
}
|
||||
return $sColumnType;
|
||||
}
|
||||
|
||||
private function getTableConstraints($sTableName)
|
||||
{
|
||||
//Primary key
|
||||
$asTableConstraints = array("PRIMARY KEY (`".self::getId($sTableName)."`)");
|
||||
|
||||
//Foreign keys: applied using ALTER TABLE syntax at the end to prevent scheduling CREATE TABLE queries
|
||||
|
||||
//Other constraints
|
||||
if(array_key_exists($sTableName, $this->asOptions['constraints'])) {
|
||||
if(is_array($this->asOptions['constraints'][$sTableName])) $asTableConstraints = array_merge($asTableConstraints, $this->asOptions['constraints'][$sTableName]);
|
||||
else $asTableConstraints[] = $this->asOptions['constraints'][$sTableName];
|
||||
}
|
||||
return $asTableConstraints;
|
||||
}
|
||||
|
||||
private function addQuotes($oData)
|
||||
{
|
||||
//TODO remake
|
||||
$asTrustedFunc = array('CURDATE()', 'NOW()', 'NULL');
|
||||
$sChar = "'";
|
||||
if(is_array($oData))
|
||||
{
|
||||
$asChar = array_fill(1, count($oData), $sChar);
|
||||
return array_combine(array_keys($oData), array_map(array($this, 'addQuotes'), $oData, $asChar));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(in_array($oData, $asTrustedFunc) || $this->isField($oData)) return $oData;
|
||||
else return $sChar.$oData.$sChar;
|
||||
}
|
||||
}
|
||||
|
||||
private function getLastId()
|
||||
{
|
||||
return $this->oConnection->insert_id;
|
||||
}
|
||||
|
||||
private function getLastImpact()
|
||||
{
|
||||
return ($this->oConnection->affected_rows > 0);
|
||||
}
|
||||
|
||||
public function insertRow($sTableName, $asData)
|
||||
{
|
||||
$this->cleanSql($sTableName);
|
||||
$this->cleanSql($asData);
|
||||
|
||||
$asQueryValues = $this->addQuotes($asData);
|
||||
$sQuery = "INSERT INTO ".$sTableName." (`".implode("`, `", array_keys($asQueryValues))."`) VALUES (".implode(", ", $asQueryValues).")";
|
||||
return $this->setQuery($sQuery)?$this->getLastId():0;
|
||||
}
|
||||
|
||||
public function updateRow($sTableName, $asConstraints, $asData, $bLedUpdate=true)
|
||||
{
|
||||
return $this->updateRows($sTableName, $asConstraints, $asData, 1, $bLedUpdate);
|
||||
}
|
||||
|
||||
public function updateRows($sTableName, $asConstraints, $asData, $iLimit=0, $bLedUpdate=true)
|
||||
{
|
||||
if(!is_array($asConstraints))
|
||||
{
|
||||
$asConstraints = array($this->getId($sTableName)=>$asConstraints);
|
||||
}
|
||||
|
||||
//Cleaning values
|
||||
$this->cleanSql($sTableName);
|
||||
$this->cleanSql($asData);
|
||||
$this->cleanSql($asConstraints);
|
||||
$asQueryValues = $this->addQuotes($asData);
|
||||
$asConstraintsValues = $this->addQuotes($asConstraints);
|
||||
$this->addColumnSelectors($asQueryValues);
|
||||
$this->addColumnSelectors($asConstraintsValues);
|
||||
|
||||
//Building query
|
||||
if(!$bLedUpdate) $asQueryValues['led'] = 'led';
|
||||
$sLimit = $iLimit>0?" LIMIT $iLimit":"";
|
||||
$sQuery = "UPDATE {$sTableName} ".
|
||||
"SET ".$this->implodeAll($asQueryValues, " = ", ", ")." ".
|
||||
"WHERE ".$this->implodeAll($asConstraintsValues, " = ", " AND ").$sLimit;
|
||||
|
||||
$iResult = false;
|
||||
if($this->setQuery($sQuery))
|
||||
{
|
||||
if(!$this->getLastImpact()) $this->addNotice('Last query had no effect on db: "'.$sQuery.';"');
|
||||
$iResult = ($iLimit==1)?$this->selectValue($sTableName, $this->getId($sTableName), $asConstraints):true;
|
||||
}
|
||||
return $iResult;
|
||||
}
|
||||
|
||||
public function insertUpdateRow($sTableName, $asData, $asKeys=array(), $bUpdate=true)
|
||||
{
|
||||
$sTableIdName = self::getId($sTableName);
|
||||
|
||||
//check for data in the db
|
||||
if($asKeys==array())
|
||||
{
|
||||
$asKeys[] = $sTableIdName;
|
||||
}
|
||||
$asValues = array_intersect_key($asData, array_flip($asKeys));
|
||||
$iTableId = $this->selectValue($sTableName, $sTableIdName, $asValues);
|
||||
|
||||
//insert
|
||||
if(!$iTableId)
|
||||
{
|
||||
$iTableId = $this->insertRow($sTableName, $asData);
|
||||
}
|
||||
//Update
|
||||
elseif($bUpdate)
|
||||
{
|
||||
if(array_key_exists($sTableIdName, $asData))
|
||||
{
|
||||
unset($asData[$sTableIdName]);
|
||||
}
|
||||
$iTableId = $this->updateRow($sTableName, $iTableId, $asData);
|
||||
}
|
||||
return $iTableId;
|
||||
}
|
||||
|
||||
public function selectInsert($sTableName, $asData, $asKeys=array())
|
||||
{
|
||||
return $this->insertUpdateRow($sTableName, $asData, $asKeys, false);
|
||||
}
|
||||
|
||||
public function deleteRow($sTableName, $iTableId)
|
||||
{
|
||||
$this->cleanSql($sTableName);
|
||||
$this->cleanSql($iTableId);
|
||||
|
||||
$bSuccess = true;
|
||||
|
||||
//linked tables
|
||||
switch($sTableName)
|
||||
{
|
||||
case (array_key_exists('cascading_delete', $this->asOptions) && array_key_exists($sTableName, $this->asOptions['cascading_delete'])) :
|
||||
$asTables = array_merge(is_array($sTableName)?array_values($sTableName):array($sTableName), array_values($this->asOptions['cascading_delete'][$sTableName]));
|
||||
break;
|
||||
case is_string($sTableName) :
|
||||
$asTables = array($sTableName);
|
||||
break;
|
||||
case is_array($sTableName):
|
||||
$asTables = $sTableName;
|
||||
break;
|
||||
default:
|
||||
$asTables = array();
|
||||
}
|
||||
foreach($asTables as $sTable)
|
||||
{
|
||||
$bSuccess = $bSuccess && $this->setQuery("DELETE FROM ".$sTable." WHERE ".$this->getId($sTableName)." = ".$iTableId);
|
||||
}
|
||||
return $bSuccess;
|
||||
}
|
||||
|
||||
public function emptyTable($sTableName)
|
||||
{
|
||||
$this->cleanSql($sTableName);
|
||||
return $this->setQuery("TRUNCATE ".$sTableName);
|
||||
}
|
||||
|
||||
public function selectList($sTableName, $sColumnName='', $asConstraints=array())
|
||||
{
|
||||
$sColumnName = $sColumnName==''?self::getText($sTableName):$sColumnName;
|
||||
$sIdColumnName = self::getId($sTableName);
|
||||
return $this->selectRows( array( 'select' => array($sIdColumnName, $sColumnName),
|
||||
'from' => $sTableName,
|
||||
'constraint'=> $asConstraints),
|
||||
true,
|
||||
$sIdColumnName);
|
||||
}
|
||||
|
||||
public function selectRows($asInfo, $sGroupBy='', $bStringOnly=true)
|
||||
{
|
||||
$asAttributes = array('select'=>"SELECT", 'from'=>"FROM", 'join'=>"LEFT JOIN", 'joinOn'=>"LEFT JOIN", 'constraint'=>"WHERE", 'groupBy'=>"GROUP BY", 'orderBy'=>"ORDER BY", 'limit'=>'LIMIT');
|
||||
$asRowSeparators = array('select'=>", ", 'from'=>"", 'join'=>" LEFT JOIN ", 'joinOn'=>" LEFT JOIN ", 'constraint'=>" AND ", 'groupBy'=>", ", 'orderBy'=>", ", 'limit'=>"");
|
||||
$asOperators = array('constraint'=>" = ", 'orderBy'=>" ", 'join'=>" USING(", 'joinOn'=>" ON ");
|
||||
$asEndOfStatement = array('constraint'=>"", 'orderBy'=>"", 'join'=>")", 'joinOn'=>"");
|
||||
|
||||
//Simple selectRows
|
||||
if(!is_array($asInfo)) $asInfo = array('from'=>$asInfo);
|
||||
|
||||
//Get table by key
|
||||
if($sGroupBy===true)
|
||||
{
|
||||
$sGroupBy = self::getId($asInfo['from']);
|
||||
|
||||
//Add id to selection
|
||||
if(isset($asInfo['select']) && $asInfo['select'][0]!="*") $asInfo['select'][] = $sGroupBy;
|
||||
}
|
||||
|
||||
$sQuery = "";
|
||||
foreach($asAttributes as $sStatement => $sKeyWord)
|
||||
{
|
||||
$asSelection = array_key_exists($sStatement, $asInfo)?$asInfo[$sStatement]:array();
|
||||
if(!is_array($asSelection))
|
||||
{
|
||||
$asSelection = array($asSelection);
|
||||
}
|
||||
|
||||
//if provided values
|
||||
if(!empty($asSelection))
|
||||
{
|
||||
$this->cleanSql($asSelection);
|
||||
|
||||
if($sStatement=='constraint' && !array_key_exists('constVar', $asInfo))
|
||||
{
|
||||
$asSelection = $this->addQuotes($asSelection);
|
||||
foreach($asSelection as $sField=>$asConstraints)
|
||||
{
|
||||
if(is_array($asConstraints))
|
||||
{
|
||||
if(array_key_exists('constOpe', $asInfo) && array_key_exists($sField, $asInfo['constOpe']) && $asInfo['constOpe'][$sField]=='BETWEEN') {
|
||||
//Between
|
||||
$asSelection[$sField] = $asConstraints['from'].' AND '.$asConstraints['to'];
|
||||
$asInfo['constOpe'][$sField] = " BETWEEN ";
|
||||
}
|
||||
else {
|
||||
//Multiple values (IN)
|
||||
$asSelection[$sField] = "(".implode(', ', $asConstraints).")";
|
||||
$asInfo['constOpe'][$sField] = " IN ";
|
||||
}
|
||||
}
|
||||
elseif(!array_key_exists('constOpe', $asInfo) || !array_key_exists($sField, $asInfo['constOpe'])) $asInfo['constOpe'][$sField] = " = ";
|
||||
}
|
||||
}
|
||||
$this->addColumnSelectors($asSelection);
|
||||
|
||||
$sQuery .= " ".$sKeyWord." ";
|
||||
|
||||
//in case of double value input
|
||||
if(array_key_exists($sStatement, $asOperators))
|
||||
{
|
||||
if($sStatement=='constraint' && array_key_exists('constOpe', $asInfo))
|
||||
{
|
||||
$asOperators[$sStatement] = $asInfo['constOpe'];
|
||||
}
|
||||
elseif($sStatement=='joinOn')
|
||||
{
|
||||
$asSimplifiedSelection = array();
|
||||
foreach($asSelection as $sTable => $asJoinFields)
|
||||
{
|
||||
$asJoinFields = $this->addQuotes($asJoinFields);
|
||||
$asSimplifiedSelection[$sTable] = $this->implodeAll($asJoinFields, " = ", " AND ");
|
||||
}
|
||||
$asSelection = $asSimplifiedSelection;
|
||||
}
|
||||
$sQuery .= $this->implodeAll($asSelection, $asOperators[$sStatement], $asRowSeparators[$sStatement], "", $asEndOfStatement[$sStatement]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sQuery .= implode($asRowSeparators[$sStatement], $asSelection);
|
||||
}
|
||||
}
|
||||
//default value for select
|
||||
elseif($sStatement=='select')
|
||||
{
|
||||
$sQuery .= " ".$sKeyWord." * ";
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getArrayQuery(trim($sQuery), $bStringOnly, $sGroupBy);
|
||||
}
|
||||
|
||||
private function addColumnSelectors(&$asSelection)
|
||||
{
|
||||
//FIXME get rid of this
|
||||
$sSqlWord = 'option';
|
||||
$sKey = array_search($sSqlWord, $asSelection);
|
||||
if($sKey!==false)
|
||||
{
|
||||
$asSelection[$sKey] = "`".$asSelection[$sKey]."`";
|
||||
}
|
||||
elseif(array_key_exists($sSqlWord, $asSelection))
|
||||
{
|
||||
$asSelection["`".$sSqlWord."`"] = $asSelection[$sSqlWord];
|
||||
unset($asSelection[$sSqlWord]);
|
||||
}
|
||||
}
|
||||
|
||||
public function selectRow($sTableName, $asConstraints=array(), $sColumnName='*')
|
||||
{
|
||||
//Table ID directly
|
||||
if(!is_array($asConstraints)) $asConstraints = array($this->getId($sTableName)=>$asConstraints);
|
||||
|
||||
$asRows = $this->selectRows(array('select'=>$sColumnName, 'from'=>$sTableName, 'constraint'=>$asConstraints));
|
||||
$iCountNb = count($asRows);
|
||||
switch($iCountNb)
|
||||
{
|
||||
case 0 :
|
||||
$asResult = array();
|
||||
break;
|
||||
case $iCountNb > 1 :
|
||||
$this->addError('More than 1 result for a selectRow(): '.$iCountNb.' results. Table: '.$sTableName.', constraint: '.self::implodeAll($asConstraints, '=', ' '));
|
||||
default:
|
||||
$asResult = array_shift($asRows);
|
||||
}
|
||||
return $asResult;
|
||||
}
|
||||
|
||||
public function selectColumn($sTableName, $asColumnNames, $asConstraints)
|
||||
{
|
||||
$sGroupBy = '';
|
||||
if(!is_array($asColumnNames)) $asColumnNames = array($asColumnNames);
|
||||
else $sGroupBy = $asColumnNames[0];
|
||||
|
||||
return $this->selectRows(
|
||||
array(
|
||||
'select' => $asColumnNames,
|
||||
'from' => $sTableName,
|
||||
'constraint'=> $asConstraints
|
||||
),
|
||||
$sGroupBy
|
||||
);
|
||||
}
|
||||
|
||||
public function selectValue($sTableName, $sColumnName, $oConstraints=array())
|
||||
{
|
||||
if(!is_array($oConstraints))
|
||||
{
|
||||
$oConstraints = array($this->getId($sTableName)=>$oConstraints);
|
||||
}
|
||||
$oResult = $this->selectRow($sTableName, $oConstraints, $sColumnName);
|
||||
return empty($oResult)?false:$oResult;
|
||||
}
|
||||
|
||||
public function selectId($sTableName, $oConstraints)
|
||||
{
|
||||
return $this->selectValue($sTableName, self::getId($sTableName), $oConstraints);
|
||||
}
|
||||
|
||||
public function pingValue($sTableName, $oConstraints)
|
||||
{
|
||||
return $this->selectValue($sTableName, 'COUNT(1)', $oConstraints);
|
||||
}
|
||||
|
||||
public function cleanSql(&$oData)
|
||||
{
|
||||
$this->cleanData($oData);
|
||||
$oData = $this->cleanData($oData);
|
||||
}
|
||||
|
||||
//TODO déplacer dans ToolBox::implodeAll
|
||||
public static function implodeAll($asText, $asKeyValueSeparator='', $sRowSeparator='', $sKeyPre='', $sValuePost=false)
|
||||
{
|
||||
if($sValuePost===false)
|
||||
{
|
||||
$sValuePost = $sKeyPre;
|
||||
}
|
||||
$asCombinedText = array();
|
||||
|
||||
//if unique value for key value separator
|
||||
if(!is_array($asKeyValueSeparator) && !empty($asText))
|
||||
{
|
||||
$asKeyValueSeparator = array_combine(array_keys($asText), array_fill(0, count($asText), $asKeyValueSeparator));
|
||||
}
|
||||
|
||||
$asFrom = array('[/KEY\]', '[/VALUE\]');
|
||||
foreach($asText as $sKey=>$sValue)
|
||||
{
|
||||
$asTo = array($sKey, $sValue);
|
||||
$sRepKeyPre = str_replace($asFrom, $asTo, $sKeyPre);
|
||||
$asRepKeyValueSeparator = str_replace($asFrom, $asTo, $asKeyValueSeparator[$sKey]);
|
||||
$sRepValuePost = str_replace($asFrom, $asTo, $sValuePost);
|
||||
$asCombinedText[] = $sRepKeyPre.$sKey.$asRepKeyValueSeparator.(is_array($sValue)?implode($sValue):$sValue).$sRepValuePost;
|
||||
}
|
||||
return implode($sRowSeparator, $asCombinedText);
|
||||
}
|
||||
|
||||
public static function arrayKeyFilter($asArray, $sCallBack)
|
||||
{
|
||||
$asValidKeys = array_flip(array_filter(array_keys($asArray), $sCallBack));
|
||||
return array_intersect_key($asArray, $asValidKeys);
|
||||
}
|
||||
|
||||
public function cleanData($oData)
|
||||
{
|
||||
if(!is_array($oData))
|
||||
{
|
||||
return $this->oConnection->real_escape_string($oData);
|
||||
}
|
||||
elseif(count($oData)>0)
|
||||
{
|
||||
$asKeys = array_map(array($this, 'cleanData'), array_keys($oData));
|
||||
$asValues = array_map(array($this, 'cleanData'), $oData);
|
||||
return array_combine($asKeys, $asValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
0
inc/rss.php → inc/Feed.php
Executable file → Normal file
0
inc/rss.php → inc/Feed.php
Executable file → Normal file
181
inc/Main.php
Normal file
181
inc/Main.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace Franzz\Objects;
|
||||
use \Settings;
|
||||
|
||||
/**
|
||||
* Main Class
|
||||
* @author franzz
|
||||
* @version 2.3
|
||||
*/
|
||||
abstract class Main extends PhpObject
|
||||
{
|
||||
//Constants
|
||||
const SUCCESS = 'success';
|
||||
const ERROR = 'error';
|
||||
const UNAUTHORIZED = 'unauthorized';
|
||||
const NOT_FOUND = 'unknown action';
|
||||
const NO_DATA = 'No Data';
|
||||
|
||||
const ACTIVE = 1;
|
||||
const INACTIVE = 0;
|
||||
|
||||
//Folders
|
||||
const MASKS_FOLDER = 'masks/';
|
||||
|
||||
/**
|
||||
* DB Handle
|
||||
* @var Db
|
||||
*/
|
||||
protected $oDb;
|
||||
|
||||
//Dates format
|
||||
const URL_DATE_FORMAT = 'Ymd';
|
||||
const LAYOUT_DATE_FORMAT = 'F \t\h\e jS, Y';
|
||||
const MYSQL_DATE_FORMAT = 'Y-m-d';
|
||||
const LAYOUT_TIME_FORMAT = 'G:i';
|
||||
|
||||
//Variables
|
||||
protected $asMasks;
|
||||
protected $asContext;
|
||||
|
||||
/**
|
||||
* Language Translator
|
||||
* @var Translator
|
||||
*/
|
||||
protected $oLang;
|
||||
|
||||
/**
|
||||
* Main constructor
|
||||
*/
|
||||
public function __construct($sProcessPage, $bDb=true, $sFile=__FILE__, $sTimeZone='')
|
||||
{
|
||||
parent::__construct($sFile, Settings::DEBUG);
|
||||
|
||||
$this->setContext($sProcessPage, $sTimeZone);
|
||||
|
||||
//Init objects
|
||||
if($bDb) {
|
||||
$asConf = array(
|
||||
'server' => Settings::DB_SERVER,
|
||||
'database' => Settings::DB_NAME,
|
||||
'user' => Settings::DB_LOGIN,
|
||||
'pass' => Settings::DB_PASS,
|
||||
'encoding' => Settings::DB_ENC
|
||||
);
|
||||
$this->oDb = new Db($asConf, $this->getSqlOptions());
|
||||
if(in_array($this->oDb->sDbState, array(Db::DB_NO_DATA, Db::DB_NO_TABLE))) $this->install();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract function install();
|
||||
|
||||
public function getDbBuildScript() {
|
||||
return $this->oDb->getFullInstallQuery();
|
||||
}
|
||||
|
||||
private function setContext($sProcessPage, $sTimeZone='')
|
||||
{
|
||||
//Browser <> PHP <> MySql synchronization
|
||||
if($sTimeZone=='') $sTimeZone = Settings::TIMEZONE;
|
||||
date_default_timezone_set($sTimeZone);
|
||||
ini_set('default_charset', Settings::TEXT_ENC);
|
||||
header('Content-Type: text/html; charset='.Settings::TEXT_ENC);
|
||||
mb_internal_encoding(Settings::TEXT_ENC);
|
||||
mb_http_output(Settings::TEXT_ENC);
|
||||
mb_http_input(Settings::TEXT_ENC);
|
||||
mb_language('uni');
|
||||
mb_regex_encoding(Settings::TEXT_ENC);
|
||||
|
||||
$this->asContext['process_page'] = basename($sProcessPage);
|
||||
|
||||
$sServerName = array_key_exists('SERVER_NAME', $_SERVER)?$_SERVER['SERVER_NAME']:$_SERVER['PWD'];
|
||||
$sScheme = array_key_exists('HTTP_X_FORWARDED_PROTO', $_SERVER)?$_SERVER['HTTP_X_FORWARDED_PROTO']:$_SERVER['REQUEST_SCHEME'];
|
||||
$sAppPath = $sScheme.'://'.str_replace(array('http://', 'https://'), '', $sServerName.dirname($_SERVER['SCRIPT_NAME']));
|
||||
$this->asContext['serv_name'] = $sAppPath.(mb_substr($sAppPath, -1)!='/'?'/':'');
|
||||
|
||||
$this->setMasks();
|
||||
}
|
||||
|
||||
public static function addTimestampToFilePath($sFilePath)
|
||||
{
|
||||
//Remove timestamp
|
||||
$sCleanedFilePath = preg_replace('/(.*)\?[\d]{14}$/', '$1', $sFilePath);
|
||||
|
||||
//Add timestamp
|
||||
return file_exists($sCleanedFilePath)?$sCleanedFilePath.'?'.date("YmdHis", filemtime($sCleanedFilePath)):$sFilePath;
|
||||
}
|
||||
|
||||
public function addUncaughtError($sError)
|
||||
{
|
||||
$this->addError('Uncaught errors:'."\n".$sError);
|
||||
}
|
||||
|
||||
/* Building main pages */
|
||||
|
||||
public function getMainPage($asGlobalVars=array(), $sMainPage='index', $asMainPageTags=array(), $asCachePages=array())
|
||||
{
|
||||
$asDefaultConsts = array('success'=>self::SUCCESS, 'error'=>self::ERROR, 'process_page'=>$this->asContext['process_page']);
|
||||
$asGlobalVars['consts'] = array_merge($asDefaultConsts, array_key_exists('consts', $asGlobalVars)?$asGlobalVars['consts']:array());
|
||||
|
||||
//Masks
|
||||
if(empty($asCachePages)) $asCachePages = array_values($this->asMasks);
|
||||
foreach($asCachePages as $sPage)
|
||||
{
|
||||
if($sPage != $sMainPage) {
|
||||
$oMask = new Mask($sPage, $this->oLang);
|
||||
$sPageContent = $oMask->getMask();
|
||||
$asGlobalVars['consts']['pages'][$sPage] = $sPageContent;
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_null($this->oLang)) {
|
||||
$asGlobalVars['consts']['lang_prefix'] = Mask::LANG_PREFIX;
|
||||
$asGlobalVars['consts']['lang'] = $this->oLang->getTranslations();
|
||||
}
|
||||
|
||||
$oMainMask = new Mask($sMainPage, $this->oLang);
|
||||
$oMainMask->setTag('GLOBAL_VARS', json_encode($asGlobalVars));
|
||||
$oMainMask->setTags($asMainPageTags);
|
||||
|
||||
return $oMainMask->getMask();
|
||||
}
|
||||
|
||||
protected function getPageContent($sPage)
|
||||
{
|
||||
return ToolBox::fixEOL(file_get_contents(self::MASKS_FOLDER.$sPage.'.html'));
|
||||
}
|
||||
|
||||
/* DB structure. See Db::__construct */
|
||||
|
||||
protected abstract function getSqlOptions();
|
||||
|
||||
private function setMasks()
|
||||
{
|
||||
//List all available masks
|
||||
$asMaskPaths = glob(Mask::getMaskFile('*'));
|
||||
$this->asMasks = array_map('basename', $asMaskPaths, array_fill(1, count($asMaskPaths), Mask::MASK_EXT));
|
||||
}
|
||||
|
||||
public static function getJsonResult($bSuccess, $sDesc, $asVars=array())
|
||||
{
|
||||
header('Content-type: application/json');
|
||||
$asResult = self::getResult($bSuccess, $sDesc, $asVars);
|
||||
$asResult['result'] = $asResult['result']?self::SUCCESS:self::ERROR;
|
||||
return json_encode($asResult);
|
||||
}
|
||||
|
||||
public static function getResult($bSuccess, $sDesc, $asVars=array()) {
|
||||
return array('result'=>$bSuccess, 'desc'=>$sDesc, 'data'=>$asVars);
|
||||
}
|
||||
|
||||
public static function goTo403() {
|
||||
http_response_code(403);
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function goTo404() {
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
316
inc/Mask.php
Normal file
316
inc/Mask.php
Normal file
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
|
||||
namespace Franzz\Objects;
|
||||
use \Settings;
|
||||
|
||||
/**
|
||||
* Mask Reader
|
||||
* @author franzz
|
||||
* @version 1.2
|
||||
*/
|
||||
class Mask extends PhpObject
|
||||
{
|
||||
public $sMaskName;
|
||||
public $sFilePath;
|
||||
private $sMask;
|
||||
private $asTags;
|
||||
private $asTagsParams;
|
||||
private $asPartsSource;
|
||||
private $aoInstances;
|
||||
|
||||
private $oLang;
|
||||
private $sTimezone;
|
||||
|
||||
const MASK_FOLDER = 'masks/';
|
||||
const MASK_EXT = '.html';
|
||||
const START_TAG = 'START';
|
||||
const END_TAG = 'END';
|
||||
const TAG_MARK = '[#]';
|
||||
const LANG_PREFIX = 'lang:';
|
||||
const TIME_PREFIX = 'time:';
|
||||
|
||||
public function __construct($sFileName='', Translator $oLang=null, $sTimezone = '')
|
||||
{
|
||||
//init
|
||||
parent::__construct(__FILE__, Settings::DEBUG);
|
||||
$this->sMaskName = '';
|
||||
$this->sFilePath = '';
|
||||
$this->sMask = '';
|
||||
$this->asTags = array();
|
||||
$this->asTagsParams = array();
|
||||
$this->asPartsSource = array();
|
||||
$this->aoInstances = array();
|
||||
$this->sFilePath = '';
|
||||
$this->setTranslator($oLang);
|
||||
$this->setTimezone($sTimezone);
|
||||
|
||||
//load file
|
||||
if($sFileName!='') $this->initFile($sFileName);
|
||||
}
|
||||
|
||||
public function setLanguage($sLang, $sDefaultLang='') {
|
||||
if(is_null($this->oLang)) $this->setTranslator(new Translator($sLang, $sDefaultLang));
|
||||
else {
|
||||
$this->oLang->setLanguage($sLang, $sDefaultLang);
|
||||
foreach($this->aoInstances as &$aoPartInstance) {
|
||||
foreach($aoPartInstance as $oInstance) $oInstance->setLanguage($sLang, $sDefaultLang);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setTimezone($sTimezone) {
|
||||
$this->sTimezone = ($sTimezone=='')?Settings::TIMEZONE:$sTimezone;
|
||||
}
|
||||
|
||||
private function setTranslator($oLang) {
|
||||
$this->oLang = $oLang;
|
||||
foreach($this->aoInstances as &$aoPartInstance) {
|
||||
foreach($aoPartInstance as $oInstance) $oInstance->setTranslator($this->oLang);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Mask Translator
|
||||
* @return Translator
|
||||
*/
|
||||
public function getTranslator() {
|
||||
return $this->oLang;
|
||||
}
|
||||
|
||||
public static function getMaskFile($sFileName)
|
||||
{
|
||||
return self::MASK_FOLDER.$sFileName.self::MASK_EXT;
|
||||
}
|
||||
|
||||
public function initFile($sFileName)
|
||||
{
|
||||
$sFilePath = self::getMaskFile(basename($sFileName));
|
||||
if(file_exists($sFilePath))
|
||||
{
|
||||
$this->sFilePath = $sFilePath;
|
||||
$sSource = file_get_contents($this->sFilePath);
|
||||
$this->initMask($sFileName, $sSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->addError('Fichier introuvable à l\'adresse : '.$sFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
public function initFileFromString($sSource, $sPartName='', $iInstanceNb=0)
|
||||
{
|
||||
$this->initMask($sPartName.' (from row) '.$iInstanceNb, $sSource);
|
||||
}
|
||||
|
||||
private function initMask($sMaskName, $sSource)
|
||||
{
|
||||
$this->sMaskName = $sMaskName;
|
||||
$this->sMask = $sSource;
|
||||
$this->setParts();
|
||||
$this->setDefaultTagValues();
|
||||
}
|
||||
|
||||
private function setParts()
|
||||
{
|
||||
while(preg_match('/\<\!-- \[PART\] (?P<part>\S+) \[START\] --\>/u', $this->sMask, $asMatch))
|
||||
{
|
||||
$sPartName = $asMatch['part'];
|
||||
|
||||
$this->asPartsSource[$sPartName] = $this->getCleanPart($sPartName);
|
||||
$this->aoInstances[$sPartName] = array();
|
||||
}
|
||||
}
|
||||
|
||||
private function getCleanPart($sPartName)
|
||||
{
|
||||
$iStartPos = $this->getPartStartPos($sPartName);
|
||||
$iEndPos = $this->getPartEndPos($sPartName);
|
||||
$sPart = mb_substr($this->sMask, $iStartPos, $iEndPos-$iStartPos);
|
||||
$sExtendedPart = $this->getPartPattern($sPartName, self::START_TAG).$sPart. $this->getPartPattern($sPartName, self::END_TAG);
|
||||
$this->sMask = str_replace($sExtendedPart, $this->getPartTagPattern($sPartName), $this->sMask);
|
||||
return $sPart;
|
||||
}
|
||||
|
||||
private function getPartStartPos($sPartName)
|
||||
{
|
||||
$sPartStartPattern = $this->getPartPattern($sPartName, self::START_TAG);
|
||||
return mb_strpos($this->sMask, $sPartStartPattern) + strlen($sPartStartPattern);
|
||||
}
|
||||
|
||||
private function getPartEndPos($sPartName)
|
||||
{
|
||||
$sPartEndPattern = $this->getPartPattern($sPartName, self::END_TAG);
|
||||
return mb_strpos($this->sMask, $sPartEndPattern);
|
||||
}
|
||||
|
||||
private function getPartPattern($sPartName, $sAction)
|
||||
{
|
||||
return '<!-- [PART] '.$sPartName.' ['.$sAction.'] -->';
|
||||
}
|
||||
|
||||
private function getPartTagPattern($sPartName, $bMark=true)
|
||||
{
|
||||
$sPartTag = 'PART '.$sPartName;
|
||||
return $bMark?$this->addTagMark($sPartTag):$sPartTag;
|
||||
}
|
||||
|
||||
public function addInstance($sPartName, $asTags)
|
||||
{
|
||||
$this->newInstance($sPartName);
|
||||
foreach($asTags as $sTagName=>$sTagValue)
|
||||
{
|
||||
$this->setInstanceTag($sPartName, $sTagName, $sTagValue);
|
||||
}
|
||||
}
|
||||
|
||||
public function newInstance($sPartName)
|
||||
{
|
||||
//Finding the part ($oMask is a pointer)
|
||||
$oMask = $this->findPart($this, $sPartName);
|
||||
if(!$oMask) $this->addError('No part found : '.$sPartName);
|
||||
|
||||
//Retrieving source html
|
||||
$sPartSource = $oMask->asPartsSource[$sPartName];
|
||||
|
||||
//Creating new instance
|
||||
$oInstance = new Mask('', $this->oLang);
|
||||
$oInstance->initFileFromString($sPartSource, $sPartName);
|
||||
$oMask->aoInstances[$sPartName][] = $oInstance;
|
||||
}
|
||||
|
||||
public function setInstanceTag($sPartName, $sTagName, $sTagValue, $asTagParams=array())
|
||||
{
|
||||
$oMask = $this->findPart($this, $sPartName);
|
||||
if(!$oMask) $this->addError('No part found : '.$sPartName);
|
||||
|
||||
$oMask->getCurrentInstance($sPartName)->setTag($sTagName, $sTagValue, $asTagParams);
|
||||
}
|
||||
|
||||
public function setInstanceTags($sPartName, $asTags)
|
||||
{
|
||||
$oMask = $this->findPart($this, $sPartName);
|
||||
if(!$oMask) $this->addError('No part found : '.$sPartName);
|
||||
|
||||
foreach($asTags as $sTagName=>$sTagValue) {
|
||||
$oMask->getCurrentInstance($sPartName)->setTag($sTagName, $sTagValue);
|
||||
}
|
||||
}
|
||||
|
||||
private function findPart($oMask, $sPartName)
|
||||
{
|
||||
if(array_key_exists($sPartName, $oMask->aoInstances))
|
||||
{
|
||||
return $oMask;
|
||||
}
|
||||
else //going deeper to find the part
|
||||
{
|
||||
foreach($oMask->aoInstances as $sLevelPartName=>$aoInstances)
|
||||
{
|
||||
if(!empty($aoInstances))
|
||||
{
|
||||
//take last instances
|
||||
$oTmpMask = $this->findPart($oMask->getCurrentInstance($sLevelPartName), $sPartName);
|
||||
if($oTmpMask) return $oTmpMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns latest instance
|
||||
* @param String $sPartName
|
||||
* @return Mask
|
||||
*/
|
||||
private function getCurrentInstance($sPartName)
|
||||
{
|
||||
if(!empty($this->aoInstances[$sPartName]))
|
||||
{
|
||||
return end($this->aoInstances[$sPartName]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setDefaultTagValues() {
|
||||
$asTagNames = $this->getTags();
|
||||
|
||||
//Default translations
|
||||
foreach($asTagNames as $sTagName) {
|
||||
if(self::isLangTag($sTagName)) $this->setTag($sTagName, $sTagName);
|
||||
}
|
||||
}
|
||||
|
||||
public function getTags()
|
||||
{
|
||||
$asMatches = array('tag'=>array());
|
||||
$sSafeTagMark = preg_quote(self::TAG_MARK);
|
||||
$sSafeLangMark = preg_quote(self::LANG_PREFIX);
|
||||
$sPattern = '/'.$sSafeTagMark.'(?P<tag>('.$sSafeLangMark.'|)[\w\-]+)'.$sSafeTagMark.'/u';
|
||||
preg_match_all($sPattern, $this->sMask, $asMatches);
|
||||
return array_unique(array_filter($asMatches['tag']));
|
||||
}
|
||||
|
||||
public function setTag($sTagName, $sTagValue, $asTagParams=array())
|
||||
{
|
||||
$this->asTags[$sTagName] = $sTagValue;
|
||||
$this->asTagsParams[$sTagName] = $asTagParams;
|
||||
}
|
||||
|
||||
public function setTags($asTags)
|
||||
{
|
||||
foreach($asTags as $sTagName=>$sTagValue) $this->setTag($sTagName, $sTagValue);
|
||||
}
|
||||
|
||||
private static function isLangTag($sTag) {
|
||||
return (mb_substr($sTag, 0, 5) == self::LANG_PREFIX);
|
||||
}
|
||||
|
||||
private static function isTimeTag($sTag) {
|
||||
return (mb_substr($sTag, 0, 5) == self::TIME_PREFIX);
|
||||
}
|
||||
|
||||
public function getMask()
|
||||
{
|
||||
$sCompletedMask = $this->sMask;
|
||||
|
||||
//Build Parts
|
||||
foreach($this->aoInstances as $sPart=>$aoParts)
|
||||
{
|
||||
$sTagValue = '';
|
||||
foreach($aoParts as $oInstance)
|
||||
{
|
||||
$sTagValue .= $oInstance->getMask();
|
||||
}
|
||||
$this->setTag($this->getPartTagPattern($sPart, false), $sTagValue);
|
||||
}
|
||||
|
||||
//Special Tags
|
||||
$asTags = $this->asTags;
|
||||
foreach($asTags as $sTagName=>&$sTagValue) {
|
||||
$sTagActValue = mb_substr($sTagValue, 5);
|
||||
|
||||
//Translate Tag
|
||||
if(!is_null($this->oLang) && self::isLangTag($sTagValue)) $sTagValue = $this->oLang->getTranslation($sTagActValue, $this->asTagsParams[$sTagName]);
|
||||
|
||||
//Convert Value to Mask Time Zone
|
||||
if(self::isTimeTag($sTagValue)) $sTagValue = (new \DateTime('@'.$sTagActValue))->setTimeZone(new \DateTimeZone($this->sTimezone))->format($this->asTagsParams[$sTagName]);;
|
||||
}
|
||||
|
||||
//Replace Tags
|
||||
if(!empty($asTags)) {
|
||||
$asMarkedTags = $this->addTagMark(array_keys($asTags));
|
||||
$sCompletedMask = str_replace($asMarkedTags, $asTags, $sCompletedMask);
|
||||
}
|
||||
return $sCompletedMask;
|
||||
}
|
||||
|
||||
private function addTagMark($oData)
|
||||
{
|
||||
return ToolBox::array_map_encapsulate($oData, self::TAG_MARK);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
192
inc/PhpObject.php
Normal file
192
inc/PhpObject.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace Franzz\Objects;
|
||||
use \Settings;
|
||||
|
||||
/**
|
||||
* PHP Object
|
||||
* @author franzz
|
||||
* @version 1.1
|
||||
*/
|
||||
class PhpObject
|
||||
{
|
||||
//Log file name
|
||||
const LOG_FILENAME = 'log.html';
|
||||
|
||||
//Message types
|
||||
const NOTICE_TAB = 'Notice';
|
||||
const WARNING_TAB = 'Warning';
|
||||
const ERROR_TAB = 'Error';
|
||||
const ALL_TAB = 'All';
|
||||
|
||||
//Extraction mode
|
||||
const MODE_ARRAY = 0;
|
||||
const MODE_TEXT = 1;
|
||||
const MODE_HTML = 2;
|
||||
const MODE_FILE = 3;
|
||||
|
||||
//Class variables
|
||||
private $asMessageStack;
|
||||
private $iExtractMode;
|
||||
private $sChildClass;
|
||||
private $bDebug;
|
||||
|
||||
function __construct($sClass='', $bDebug=false, $iExtractMode=self::MODE_FILE)
|
||||
{
|
||||
$this->asMessageStack = array();
|
||||
$this->resetMessageStack();
|
||||
$this->setDebug($bDebug);
|
||||
$this->setExtractMode($iExtractMode);
|
||||
$this->sChildClass = $sClass;
|
||||
}
|
||||
|
||||
public function setExtractMode($iExtractMode) {
|
||||
$this->iExtractMode = $iExtractMode;
|
||||
}
|
||||
|
||||
/* private function, use Settings::DEBUG for global setting */
|
||||
private function setDebug($bDebug)
|
||||
{
|
||||
$this->bDebug = $bDebug;
|
||||
}
|
||||
|
||||
public function getDebug()
|
||||
{
|
||||
return $this->bDebug;
|
||||
}
|
||||
|
||||
private static function getLogPath()
|
||||
{
|
||||
return dirname(__FILE__).'/'.self::LOG_FILENAME;
|
||||
}
|
||||
|
||||
private function resetMessageStack($sType=self::ALL_TAB)
|
||||
{
|
||||
if($sType==self::ALL_TAB)
|
||||
{
|
||||
$this->resetMessageStack(self::NOTICE_TAB);
|
||||
$this->resetMessageStack(self::WARNING_TAB);
|
||||
$this->resetMessageStack(self::ERROR_TAB);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->asMessageStack[$sType] = array();
|
||||
}
|
||||
}
|
||||
|
||||
protected function addNotice($sNotice)
|
||||
{
|
||||
$this->addMessage(self::NOTICE_TAB, $sNotice);
|
||||
}
|
||||
|
||||
protected function addWarning($sWarning)
|
||||
{
|
||||
$this->addMessage(self::WARNING_TAB, $sWarning);
|
||||
}
|
||||
|
||||
protected function addError($sError)
|
||||
{
|
||||
$this->addMessage(self::ERROR_TAB, $sError);
|
||||
}
|
||||
|
||||
private function addMessage($sType, $sMessage)
|
||||
{
|
||||
$this->asMessageStack[$sType][] = array('msg'=>$sMessage, 'time'=>date('d.m.Y H:i:s'), 'type'=>$sType, 'location'=>$this->sChildClass);
|
||||
}
|
||||
|
||||
protected function getCleanMessageStack($sType=self::ALL_TAB)
|
||||
{
|
||||
if($sType==self::ALL_TAB) {
|
||||
$asMessages = array_merge( //No overlap, numeric keys
|
||||
$this->asMessageStack[self::NOTICE_TAB],
|
||||
$this->asMessageStack[self::WARNING_TAB],
|
||||
$this->asMessageStack[self::ERROR_TAB]
|
||||
);
|
||||
}
|
||||
else $asMessages = $this->asMessageStack[$sType];
|
||||
|
||||
$this->resetMessageStack($sType);
|
||||
return $this->glueMessages($asMessages);
|
||||
}
|
||||
|
||||
protected function cleanMessageStack()
|
||||
{
|
||||
$sErrorStack = $this->getCleanMessageStack($this->getDebug()?self::ALL_TAB:self::ERROR_TAB);
|
||||
if($sErrorStack!='')
|
||||
{
|
||||
switch($this->iExtractMode)
|
||||
{
|
||||
case self::MODE_TEXT:
|
||||
echo $sErrorStack;
|
||||
break;
|
||||
case self::MODE_HTML:
|
||||
echo $sErrorStack;
|
||||
break;
|
||||
case self::MODE_ARRAY:
|
||||
break;
|
||||
case self::MODE_FILE:
|
||||
@file_put_contents(self::getLogPath(), "\n".$sErrorStack, FILE_APPEND);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
protected function getCleanMessageStacks($aoExtsources, $sType=self::ALL_TAB)
|
||||
{
|
||||
$aoExtsources[] = $this;
|
||||
$aoMessages = array();
|
||||
foreach($aoExtsources as $oExtSource)
|
||||
{
|
||||
$oMessages = $oExtSource->getCleanMessageStack($sType);
|
||||
if($oMessages!='')
|
||||
{
|
||||
$aoMessages[get_class($oExtSource)] = $oMessages;
|
||||
}
|
||||
}
|
||||
return $this->glueMessages($aoMessages);
|
||||
}
|
||||
*/
|
||||
|
||||
private function glueMessages($asMessages)
|
||||
{
|
||||
switch($this->iExtractMode)
|
||||
{
|
||||
case self::MODE_TEXT:
|
||||
$oMessageStack = self::recursiveImplode("\n", $asMessages);
|
||||
break;
|
||||
case self::MODE_HTML:
|
||||
$oMessageStack = self::recursiveImplode('<br />', $asMessages);
|
||||
break;
|
||||
case self::MODE_ARRAY:
|
||||
$oMessageStack = $asMessages;
|
||||
break;
|
||||
case self::MODE_FILE:
|
||||
$oMessageStack = self::recursiveImplode("\n", $asMessages);
|
||||
break;
|
||||
}
|
||||
return $oMessageStack;
|
||||
}
|
||||
|
||||
private static function flattenMessageStack($asTab)
|
||||
{
|
||||
$asFlatTab = array();
|
||||
foreach($asTab as $asRow) {
|
||||
$asFlatTab[] = '['.$asRow['time'].'] '.$asRow['type'].' - '.$asRow['location'].' - '.$asRow['msg'];
|
||||
}
|
||||
return $asFlatTab;
|
||||
}
|
||||
|
||||
private static function recursiveImplode($sGlue, $asTab)
|
||||
{
|
||||
$asTab = self::flattenMessageStack($asTab);
|
||||
return implode($sGlue, $asTab);
|
||||
}
|
||||
|
||||
function __destruct()
|
||||
{
|
||||
$this->cleanMessageStack();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
768
inc/Toolbox.php
Normal file
768
inc/Toolbox.php
Normal file
@@ -0,0 +1,768 @@
|
||||
<?php
|
||||
|
||||
namespace Franzz\Objects;
|
||||
use \Settings;
|
||||
|
||||
/**
|
||||
* ToolBox - Only static functions missing from php library
|
||||
* @author franzz
|
||||
* @version 1.1
|
||||
*/
|
||||
class ToolBox
|
||||
{
|
||||
const FILE_EOL = "\n";
|
||||
const MAIL_SUCCESS = 2;
|
||||
|
||||
public static function cleanPost(&$asData)
|
||||
{
|
||||
//get rid of magic quotes
|
||||
if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
|
||||
{
|
||||
$asData = self::cleanData($asData, 'stripslashes');
|
||||
}
|
||||
}
|
||||
|
||||
public static function cleanData($oData, $sCleaningFunc)
|
||||
{
|
||||
if(!is_array($oData))
|
||||
{
|
||||
return call_user_func($sCleaningFunc, $oData);
|
||||
}
|
||||
elseif(count($oData)>0)
|
||||
{
|
||||
$asCleaningFunc = array_fill(1, count($oData), $sCleaningFunc);
|
||||
$asKeys = array_map(array('self', 'cleanData'), array_keys($oData), $asCleaningFunc);
|
||||
$asValues = array_map(array('self', 'cleanData'), $oData, $asCleaningFunc);
|
||||
return array_combine($asKeys, $asValues);
|
||||
}
|
||||
}
|
||||
|
||||
public static function fixGlobalVars($argv)
|
||||
{
|
||||
//Add CLI arguments
|
||||
if(defined('STDIN')) mb_parse_str(implode('&', array_slice($argv, 1)), $_GET);
|
||||
$_REQUEST = array_merge($_GET, $_REQUEST);
|
||||
}
|
||||
|
||||
public static function array_map_encapsulate($oData, $sChar)
|
||||
{
|
||||
if(is_array($oData))
|
||||
{
|
||||
$asChar = array_fill(1, count($oData), $sChar);
|
||||
return array_combine(array_keys($oData), array_map(array('self', 'array_map_encapsulate'), $oData, $asChar));
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sChar.$oData.$sChar;
|
||||
}
|
||||
}
|
||||
|
||||
public static function capitalizeWords($acText, $sCharList = '')
|
||||
{
|
||||
//Use ucwords if no delimiters are given
|
||||
if($sCharList=='')
|
||||
{
|
||||
return Toolbox::mb_ucwords($acText);
|
||||
}
|
||||
|
||||
// Go through all characters
|
||||
$capitalizeNext = true;
|
||||
$max = mb_strlen($acText);
|
||||
for ($i = 0; $i < $max; $i++)
|
||||
{
|
||||
if(mb_strpos($sCharList, $acText[$i]) !== false)
|
||||
{
|
||||
$capitalizeNext = true;
|
||||
}
|
||||
elseif($capitalizeNext)
|
||||
{
|
||||
$capitalizeNext = false;
|
||||
$acText[$i] = mb_strtoupper($acText[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return $acText;
|
||||
}
|
||||
|
||||
public static function jsonExport($asData)
|
||||
{
|
||||
header('Content-type: application/json');
|
||||
//return htmlspecialchars(json_encode($asData), ENT_NOQUOTES);
|
||||
return self::jsonConvert($asData);
|
||||
}
|
||||
|
||||
public static function jsonConvert($asData)
|
||||
{
|
||||
return json_encode($asData);
|
||||
}
|
||||
|
||||
public static function curl($sUrl, $bHeader=false, $asPostData=array(), $sReturnType='text', $sCookie='', $sCreds='') {
|
||||
$oCurl = curl_init();
|
||||
curl_setopt($oCurl, CURLOPT_URL, $sUrl);
|
||||
curl_setopt($oCurl, CURLOPT_VERBOSE, false);
|
||||
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
curl_setopt($oCurl, CURLOPT_HEADER, $bHeader);
|
||||
if($bHeader) curl_setopt($oCurl, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
if(!empty($asPostData)) {
|
||||
curl_setopt($oCurl, CURLOPT_POST, 1);
|
||||
curl_setopt($oCurl, CURLOPT_POSTFIELDS, $asPostData);
|
||||
}
|
||||
|
||||
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($oCurl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
|
||||
|
||||
if($sCookie!='') curl_setopt($oCurl, CURLOPT_COOKIE, $sCookie);
|
||||
if($sCreds!='') curl_setopt($oCurl, CURLOPT_USERPWD, $sCreds);
|
||||
|
||||
$sContent = curl_exec($oCurl);
|
||||
|
||||
$sCurlErrorId = curl_errno($oCurl);
|
||||
$sHttpCode = curl_getinfo($oCurl, CURLINFO_HTTP_CODE);
|
||||
$bSuccess = ($sCurlErrorId==0 && $sHttpCode==200);
|
||||
$sDesc = '';
|
||||
if(!$bSuccess) $sDesc = ($sCurlErrorId==0)?('HTTP Error Code '.$sHttpCode):($sCurlErrorId.': '.curl_strerror($sCurlErrorId));
|
||||
curl_close($oCurl);
|
||||
|
||||
switch($sReturnType) {
|
||||
case 'json': $oContent = json_decode($sContent, true); break;
|
||||
default: $oContent = $sContent;
|
||||
}
|
||||
|
||||
return array('result'=>$bSuccess, 'desc'=>$sDesc, 'content'=>$oContent);
|
||||
}
|
||||
|
||||
public static function getMimeType($sPath, $bSubTypeOnly=false)
|
||||
{
|
||||
$sMimetype = '';
|
||||
//Retrieving file Content Type
|
||||
if(file_exists($sPath)) //Local
|
||||
{
|
||||
$oFileInfo = new \finfo(FILEINFO_MIME);
|
||||
$sMimetype = $oFileInfo->file($sPath);
|
||||
}
|
||||
else //Remote
|
||||
{
|
||||
//get_headers($sUrl, 1)
|
||||
$oCurl = curl_init($sPath);
|
||||
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($oCurl, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($oCurl, CURLOPT_HEADER, true);
|
||||
//curl_setopt($oCurl, CURLOPT_NOBODY, true);
|
||||
curl_setopt($oCurl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
|
||||
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_exec($oCurl);
|
||||
$sMimetype = curl_getinfo($oCurl, CURLINFO_CONTENT_TYPE);
|
||||
curl_close($oCurl);
|
||||
}
|
||||
|
||||
//Only sub type (after /)
|
||||
if($bSubTypeOnly)
|
||||
{
|
||||
preg_match('`\/(?P<type>\w+)(\;|$)`ui', mb_strtolower($sMimetype), $asMatch);
|
||||
if(array_key_exists('type', $asMatch)) $sMimetype = $asMatch['type'];
|
||||
}
|
||||
return $sMimetype;
|
||||
}
|
||||
|
||||
public static function isAnimatedGif($sFilePath)
|
||||
{
|
||||
if(!($oFile = @fopen($sFilePath, 'rb'))) return false;
|
||||
|
||||
$iCount = 0;
|
||||
while (!feof($oFile) && $iCount < 2)
|
||||
{
|
||||
|
||||
$sChunk = fread($oFile, 1024 * 100); //read 100kb at a time
|
||||
$iCount += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $sChunk, $asMatches);
|
||||
}
|
||||
fclose($oFile);
|
||||
|
||||
return $iCount > 1;
|
||||
}
|
||||
|
||||
public static function createThumbnail($sInPath, $iMaxWidth, $iMaxHeight=0, $sOutPath='', $bDeleteIn=false, $asImageExts=array('jpg', 'jpeg', 'gif', 'png'), $bCrop=false, $bCopyExif=false)
|
||||
{
|
||||
$asResult = array('error'=>'');
|
||||
|
||||
//Look up the file type to choose the image creator
|
||||
$sImageExt = self::getMimeType($sInPath, true);
|
||||
$sImageExt = ($sImageExt=='jpg')?'jpeg':$sImageExt;
|
||||
|
||||
//New Destination folder
|
||||
$asInInfo = pathinfo($sInPath);
|
||||
$asOutInfo = pathinfo($sOutPath);
|
||||
if($sOutPath=='') $sOutPath = $sInPath;
|
||||
elseif(mb_substr($sOutPath, -1)=='/') $sOutPath .= mb_strtolower($asInInfo['basename']); //folder only, keep original name
|
||||
elseif(!array_key_exists('extension', $asOutInfo)) $sOutPath .= '.'.$sImageExt; //folder + filename, but getting ext from file info
|
||||
|
||||
//New sizes
|
||||
if(!in_array($sImageExt, $asImageExts) && !empty($asImageExts)) $asResult['error'] = 'Wrong file type: '.$sImageExt;
|
||||
elseif($iMaxWidth==0 && $iMaxHeight==0) $asResult['error'] = 'At least one dimension must be resized (width and/or height)';
|
||||
else
|
||||
{
|
||||
//Recalculate max width/height in case of rotated picture
|
||||
$sRotate = 0;
|
||||
$asExif = @exif_read_data($sInPath);
|
||||
if($asExif && array_key_exists('Orientation', $asExif)) {
|
||||
switch($asExif['Orientation'])
|
||||
{
|
||||
case 3: $sRotate = 180; break; //Flip over
|
||||
case 6: $sRotate = -90; break; //Clockwise
|
||||
case 8: $sRotate = 90; break; //Trigo
|
||||
}
|
||||
}
|
||||
|
||||
list($iWidth, $iHeight) = getimagesize($sInPath);
|
||||
if(abs($sRotate) == 90) {
|
||||
$iTempWidth = $iWidth;
|
||||
$iWidth = $iHeight;
|
||||
$iHeight = $iTempWidth;
|
||||
}
|
||||
|
||||
//Limit on only 1 parameter
|
||||
if($iMaxWidth==0) $iMaxWidth = $iWidth * $iMaxHeight / $iHeight;
|
||||
elseif($iMaxHeight==0) $iMaxHeight = $iHeight * $iMaxWidth / $iWidth;
|
||||
|
||||
if($iWidth > $iMaxWidth || $iHeight > $iMaxHeight)
|
||||
{
|
||||
$dResizeDeltaWidth = $iWidth - $iMaxWidth;
|
||||
$dResizeDeltaHeight = $iHeight - $iMaxHeight;
|
||||
$iPosLeft = $iPosTop = 0;
|
||||
$iThumbWidth = $iMaxWidth;
|
||||
$iThumbHeight = $iMaxHeight;
|
||||
|
||||
//Max up the lowest value between height and width and crop the other
|
||||
if($bCrop)
|
||||
{
|
||||
if($dResizeDeltaWidth > $dResizeDeltaHeight) //Crop width
|
||||
{
|
||||
$iResizedHeight = $iMaxHeight;
|
||||
$iResizedWidth = ($iResizedHeight / $iHeight) * $iWidth;
|
||||
$iPosLeft = ($iResizedWidth - $iMaxWidth)/2*(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iResizedWidth = $iMaxWidth;
|
||||
$iResizedHeight = ($iResizedWidth / $iWidth) * $iHeight;
|
||||
$iPosTop = ($iResizedHeight - $iMaxHeight)/2*(-1);
|
||||
}
|
||||
}
|
||||
else //Just resize
|
||||
{
|
||||
if($dResizeDeltaWidth > $dResizeDeltaHeight)
|
||||
{
|
||||
$iResizedWidth = $iMaxWidth;
|
||||
$iResizedHeight = ($iResizedWidth / $iWidth) * $iHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iResizedHeight = $iMaxHeight;
|
||||
$iResizedWidth = ($iResizedHeight / $iHeight) * $iWidth;
|
||||
}
|
||||
$iThumbWidth = $iResizedWidth;
|
||||
$iThumbHeight = $iResizedHeight;
|
||||
}
|
||||
|
||||
if($sImageExt=='gif' && self::isAnimatedGif($sInPath))
|
||||
{
|
||||
$sContent = file_get_contents($sInPath);
|
||||
$sBigGifPath = uniqid().$sOutPath.'.big';
|
||||
$sCoalescePath = uniqid().$sOutPath.'.coalesce';
|
||||
|
||||
if(file_put_contents($sBigGifPath, $sContent)===false) $asResult['error'] = 'Unable to create temporary thumbnail : '.$sBigGifPath;
|
||||
system('convert '.$sBigGifPath.' -coalesce '.$sCoalescePath);
|
||||
system('convert -size '.$iWidth.'x'.$iHeight.' '.$sCoalescePath.' -resize '.$iResizedWidth.'x'.$iResizedHeight.' '.$sOutPath);
|
||||
unlink($sBigGifPath);
|
||||
unlink($sCoalescePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
//create image from source
|
||||
$oSource = call_user_func('imagecreatefrom'.$sImageExt, $sInPath);
|
||||
|
||||
//Fix rotation
|
||||
if($sRotate) $oSource = imagerotate($oSource, $sRotate, 0);
|
||||
|
||||
//Resize
|
||||
$oThumb = imagecreatetruecolor($iThumbWidth, $iThumbHeight);
|
||||
imagecopyresampled($oThumb, $oSource, $iPosLeft, $iPosTop, 0, 0, $iResizedWidth, $iResizedHeight, $iWidth, $iHeight);
|
||||
|
||||
//Save
|
||||
if(file_exists($sOutPath)) unlink($sOutPath);
|
||||
if(!call_user_func_array('image'.$sImageExt, array($oThumb, $sOutPath)))
|
||||
{
|
||||
$asResult['error'] = 'Unable to create thumbnail : '.$sOutPath;
|
||||
}
|
||||
imagedestroy($oThumb);
|
||||
}
|
||||
}
|
||||
elseif($sInPath != $sOutPath)
|
||||
{
|
||||
$iResizedWidth = $iWidth;
|
||||
$iResizedHeight = $iHeight;
|
||||
if(!copy($sInPath, $sOutPath)) $asResult['error'] = 'Copy failed from '.$sInPath.' to '.$sOutPath;
|
||||
}
|
||||
$asResult['width'] = $iResizedWidth;
|
||||
$asResult['height'] = $iResizedHeight;
|
||||
$asResult['out'] = $sOutPath;
|
||||
}
|
||||
|
||||
if($bCopyExif && $asResult['error'] == '') self::copyExif($sInPath, $sOutPath);
|
||||
if($bDeleteIn && $asResult['error'] == '' && $sInPath != $sOutPath) unlink($sInPath);
|
||||
|
||||
return $asResult;
|
||||
}
|
||||
|
||||
public static function copyExif($srcfile, $destfile) {
|
||||
// Function transfers EXIF (APP1) and IPTC (APP13) from $srcfile and adds it to $destfile
|
||||
// JPEG file has format 0xFFD8 + [APP0] + [APP1] + ... [APP15] + <image data> where [APPi] are optional
|
||||
// Segment APPi (where i=0x0 to 0xF) has format 0xFFEi + 0xMM + 0xLL + <data> (where 0xMM is
|
||||
// most significant 8 bits of (strlen(<data>) + 2) and 0xLL is the least significant 8 bits
|
||||
// of (strlen(<data>) + 2)
|
||||
|
||||
if (file_exists($srcfile) && file_exists($destfile)) {
|
||||
$srcsize = @getimagesize($srcfile, $imageinfo);
|
||||
// Prepare EXIF data bytes from source file
|
||||
$exifdata = (is_array($imageinfo) && key_exists("APP1", $imageinfo)) ? $imageinfo['APP1'] : null;
|
||||
if ($exifdata) {
|
||||
$exiflength = strlen($exifdata) + 2;
|
||||
if ($exiflength > 0xFFFF) return false;
|
||||
// Construct EXIF segment
|
||||
$exifdata = chr(0xFF) . chr(0xE1) . chr(($exiflength >> 8) & 0xFF) . chr($exiflength & 0xFF) . $exifdata;
|
||||
}
|
||||
// Prepare IPTC data bytes from source file
|
||||
$iptcdata = (is_array($imageinfo) && key_exists("APP13", $imageinfo)) ? $imageinfo['APP13'] : null;
|
||||
if ($iptcdata) {
|
||||
$iptclength = strlen($iptcdata) + 2;
|
||||
if ($iptclength > 0xFFFF) return false;
|
||||
// Construct IPTC segment
|
||||
$iptcdata = chr(0xFF) . chr(0xED) . chr(($iptclength >> 8) & 0xFF) . chr($iptclength & 0xFF) . $iptcdata;
|
||||
}
|
||||
$destfilecontent = @file_get_contents($destfile);
|
||||
if (!$destfilecontent) return false;
|
||||
if (strlen($destfilecontent) > 0) {
|
||||
$destfilecontent = substr($destfilecontent, 2);
|
||||
$portiontoadd = chr(0xFF) . chr(0xD8); // Variable accumulates new & original IPTC application segments
|
||||
$exifadded = !$exifdata;
|
||||
$iptcadded = !$iptcdata;
|
||||
|
||||
while ((substr($destfilecontent, 0, 2) & 0xFFF0) === 0xFFE0) {
|
||||
$segmentlen = (substr($destfilecontent, 2, 2) & 0xFFFF);
|
||||
$iptcsegmentnumber = (substr($destfilecontent, 1, 1) & 0x0F); // Last 4 bits of second byte is IPTC segment #
|
||||
if ($segmentlen <= 2) return false;
|
||||
$thisexistingsegment = substr($destfilecontent, 0, $segmentlen + 2);
|
||||
if ((1 <= $iptcsegmentnumber) && (!$exifadded)) {
|
||||
$portiontoadd .= $exifdata;
|
||||
$exifadded = true;
|
||||
if (1 === $iptcsegmentnumber) $thisexistingsegment = '';
|
||||
}
|
||||
if ((13 <= $iptcsegmentnumber) && (!$iptcadded)) {
|
||||
$portiontoadd .= $iptcdata;
|
||||
$iptcadded = true;
|
||||
if (13 === $iptcsegmentnumber) $thisexistingsegment = '';
|
||||
}
|
||||
$portiontoadd .= $thisexistingsegment;
|
||||
$destfilecontent = substr($destfilecontent, $segmentlen + 2);
|
||||
}
|
||||
if (!$exifadded) $portiontoadd .= $exifdata; // Add EXIF data if not added already
|
||||
if (!$iptcadded) $portiontoadd .= $iptcdata; // Add IPTC data if not added already
|
||||
$outputfile = fopen($destfile, 'w');
|
||||
if ($outputfile) return fwrite($outputfile, $portiontoadd . $destfilecontent); else return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function utf8_compliant($sText)
|
||||
{
|
||||
if(strlen($sText) == 0) return true;
|
||||
return (preg_match('/^.{1}/us', $sText, $ar) == 1);
|
||||
}
|
||||
|
||||
public static function mb_ucwords($sText)
|
||||
{
|
||||
return mb_convert_case($sText, MB_CASE_TITLE, "UTF-8");
|
||||
}
|
||||
|
||||
public static function mb_ucfirst($sText)
|
||||
{
|
||||
$sLength = mb_strlen($sText);
|
||||
$sFirstChar = mb_substr($sText, 0, 1);
|
||||
$sThen = mb_substr($sText, 1, $sLength - 1);
|
||||
return mb_strtoupper($sFirstChar).$sThen;
|
||||
}
|
||||
|
||||
public static function file_get_contents_utf8($oFile)
|
||||
{
|
||||
$sContent = file_get_contents($oFile);
|
||||
return mb_convert_encoding($sContent, 'UTF-8', mb_detect_encoding($sContent, 'UTF-8, ISO-8859-1', true));
|
||||
}
|
||||
|
||||
public static function rgbToHex($R, $G, $B)
|
||||
{
|
||||
$R = dechex($R);
|
||||
if(strlen($R)<2) $R='0'.$R;
|
||||
|
||||
$G = dechex($G);
|
||||
if(strlen($G)<2) $G='0'.$G;
|
||||
|
||||
$B = dechex($B);
|
||||
if(strlen($B)<2) $B='0'.$B;
|
||||
|
||||
return $R.$G.$B;
|
||||
}
|
||||
|
||||
public static function setCookie($sCookieName, $sCookieValue, $iDays)
|
||||
{
|
||||
$iTimeLimit = time()+60*60*24*$iDays;
|
||||
setcookie($sCookieName, $sCookieValue, $iTimeLimit);
|
||||
}
|
||||
|
||||
public static function fixEOL($sText)
|
||||
{
|
||||
//Normalize line endings
|
||||
//Convert all line-endings to UNIX format
|
||||
$sText = str_replace("\r\n", "\n", $sText); //Windows
|
||||
$sText = str_replace("\r", "\n", $sText); //Mac
|
||||
|
||||
// Don't allow out-of-control blank lines
|
||||
$sText = preg_replace("/\n{2,}/", "\n\n", $sText);
|
||||
return $sText;
|
||||
}
|
||||
|
||||
public static function getUserLanguage($available_languages, $sDefaultLang='') {
|
||||
$http_accept_language = isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])?$_SERVER['HTTP_ACCEPT_LANGUAGE']:'';
|
||||
|
||||
//Format: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
||||
preg_match_all("/([[:alpha:]]{1,8})(-([[:alpha:]|-]{1,8}))?(\s*;\s*q\s*=\s*(1\.0{0,3}|0\.\d{0,3}))?\s*(,|$)/i", $http_accept_language, $hits, PREG_SET_ORDER);
|
||||
|
||||
$bestlang = $sDefaultLang;
|
||||
$bestqval = 0;
|
||||
|
||||
foreach($hits as $arr) {
|
||||
$langprefix = strtolower($arr[1]);
|
||||
if(!empty($arr[3])) {
|
||||
$langrange = strtolower($arr[3]);
|
||||
$language = $langprefix.'-'.$langrange;
|
||||
}
|
||||
else $language = $langprefix;
|
||||
|
||||
//Q Value
|
||||
$qvalue = 1.0;
|
||||
if(!empty($arr[5])) $qvalue = floatval($arr[5]);
|
||||
|
||||
//find q-maximal language
|
||||
if(in_array($language, $available_languages) && $qvalue > $bestqval) {
|
||||
$bestlang = $language;
|
||||
$bestqval = $qvalue;
|
||||
}
|
||||
//if no direct hit, try the prefix only but decrease q-value by 10% (as http_negotiate_language does)
|
||||
elseif(in_array($langprefix, $available_languages) && ($qvalue * 0.9) > $bestqval) {
|
||||
$bestlang = $langprefix;
|
||||
$bestqval = $qvalue * 0.9;
|
||||
}
|
||||
}
|
||||
return $bestlang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return relative time description
|
||||
* FIXME shitty implementation of i18n
|
||||
* @param int $oTime Time (strtotime)
|
||||
* @param string $sLang Language (en/fr)
|
||||
* @return string Relative Time
|
||||
*/
|
||||
public static function getDateTimeDesc($oTime, $sLang='en')
|
||||
{
|
||||
$iTimeStamp = is_numeric($oTime)?$oTime:strtotime($oTime);
|
||||
$sCurTimeStamp = time();
|
||||
|
||||
switch ($sLang) {
|
||||
case 'en':
|
||||
$asWeekDays = array('monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'satursday', 'sunday');
|
||||
$asMonths = array('january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december');
|
||||
break;
|
||||
case 'fr':
|
||||
$asWeekDays = array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche');
|
||||
$asMonths = array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre');
|
||||
break;
|
||||
}
|
||||
|
||||
$sSep = '|';
|
||||
$sFormat = 'Y'.$sSep.'n'.$sSep.'W'.$sSep.'N'.$sSep.'j'.$sSep.'G';
|
||||
list($sYear, $sMonth, $sWeek, $sWeekDay, $sDay, $sHour) = explode($sSep, date($sFormat, $iTimeStamp));
|
||||
list($sCurYear, $sCurMonth, $sCurWeek, $sCurWeekDay, $sCurDay, $sCurHour) = explode($sSep, date($sFormat, $sCurTimeStamp));
|
||||
|
||||
$sDesc = '';
|
||||
switch ($sLang) {
|
||||
case 'en':
|
||||
if($iTimeStamp>$sCurTimeStamp) $sDesc = 'in the future';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60) $sDesc = 'a few seconds ago';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*10) $sDesc = 'a few minutes ago';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*20) $sDesc = '15 minutes ago';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*50) $sDesc = 'half an hour ago';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*2) $sDesc = 'an hour ago';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24 && $sDay==$sCurDay) $sDesc = 'at '.date('gA', $iTimeStamp);
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24) $sDesc = 'yesterday';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*7 && $sWeek==$sCurWeek) $sDesc = $asWeekDays[$sWeekDay-1];
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*7) $sDesc = 'last '.$asWeekDays[$sWeekDay-1];
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*9) $sDesc = 'a week ago';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*12) $sDesc = '10 days ago';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*16) $sDesc = '2 weeks ago';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*23) $sDesc = '3 weeks ago';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*31 && $sMonth==$sCurMonth) $sDesc = 'on '.$asMonths[$sMonth-1].', '.$sDay;
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*30*2 && $sMonth==($sCurMonth-1)) $sDesc = 'last month';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*365 && $sYear==$sCurYear) $sDesc = 'in '.$asMonths[$sMonth-1];
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*365) $sDesc = 'in '.$asMonths[$sMonth-1].' '.$sYear;
|
||||
elseif($sYear==($sCurYear-1)) $sDesc = 'last year';
|
||||
else $sDesc = 'in '.$sYear;
|
||||
break;
|
||||
case 'fr':
|
||||
if($iTimeStamp>$sCurTimeStamp) $sDesc = 'dans le futur';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60) $sDesc = 'il y a quelques secondes';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*10) $sDesc = 'il y a quelques minutes';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*20) $sDesc = 'il y a un quart d\'heure';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*50) $sDesc = 'il y a une demi-heure';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*2) $sDesc = 'il y a une heure';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24 && $sDay==$sCurDay) $sDesc = 'à '.$sHour.'h';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24) $sDesc = 'hier';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*7 && $sWeek==$sCurWeek) $sDesc = $asWeekDays[$sWeekDay-1];
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*7) $sDesc = $asWeekDays[$sWeekDay-1].' dernier';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*9) $sDesc = 'il y a une semaine';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*12) $sDesc = 'il y a 10 jours';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*16) $sDesc = 'il y a 2 semaines';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*23) $sDesc = 'il y a 3 semaines';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*31 && $sMonth==$sCurMonth) $sDesc = 'le '.$sDay.' '.$asMonths[$sMonth-1];
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*30*2 && $sMonth==($sCurMonth-1)) $sDesc = 'le mois dernier';
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*365 && $sYear==$sCurYear) $sDesc = 'en '.$asMonths[$sMonth-1];
|
||||
elseif($sCurTimeStamp-$iTimeStamp<60*60*24*365) $sDesc = 'en '.$asMonths[$sMonth-1].' '.$sYear;
|
||||
elseif($sYear==($sCurYear-1)) $sDesc = 'l\'année dernière';
|
||||
else $sDesc = 'en '.$sYear;
|
||||
break;
|
||||
}
|
||||
|
||||
//return self::mb_ucfirst($sDesc);
|
||||
return $sDesc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Worpress function to remove accents
|
||||
* https://core.trac.wordpress.org/browser/trunk/src/wp-includes/formatting.php
|
||||
* @param String $string
|
||||
* @return String
|
||||
*/
|
||||
public static function remove_accents($string)
|
||||
{
|
||||
if(!preg_match('/[\x80-\xff]/', $string)) return $string;
|
||||
|
||||
$chars = array(
|
||||
// Decompositions for Latin-1 Supplement
|
||||
chr(194).chr(170) => 'a', chr(194).chr(186) => 'o',
|
||||
chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
|
||||
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
|
||||
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
|
||||
chr(195).chr(134) => 'AE',chr(195).chr(135) => 'C',
|
||||
chr(195).chr(136) => 'E', chr(195).chr(137) => 'E',
|
||||
chr(195).chr(138) => 'E', chr(195).chr(139) => 'E',
|
||||
chr(195).chr(140) => 'I', chr(195).chr(141) => 'I',
|
||||
chr(195).chr(142) => 'I', chr(195).chr(143) => 'I',
|
||||
chr(195).chr(144) => 'D', chr(195).chr(145) => 'N',
|
||||
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
|
||||
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
|
||||
chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
|
||||
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
|
||||
chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
|
||||
chr(195).chr(158) => 'TH',chr(195).chr(159) => 's',
|
||||
chr(195).chr(160) => 'a', chr(195).chr(161) => 'a',
|
||||
chr(195).chr(162) => 'a', chr(195).chr(163) => 'a',
|
||||
chr(195).chr(164) => 'a', chr(195).chr(165) => 'a',
|
||||
chr(195).chr(166) => 'ae',chr(195).chr(167) => 'c',
|
||||
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
|
||||
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
|
||||
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
|
||||
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
|
||||
chr(195).chr(176) => 'd', chr(195).chr(177) => 'n',
|
||||
chr(195).chr(178) => 'o', chr(195).chr(179) => 'o',
|
||||
chr(195).chr(180) => 'o', chr(195).chr(181) => 'o',
|
||||
chr(195).chr(182) => 'o', chr(195).chr(184) => 'o',
|
||||
chr(195).chr(185) => 'u', chr(195).chr(186) => 'u',
|
||||
chr(195).chr(187) => 'u', chr(195).chr(188) => 'u',
|
||||
chr(195).chr(189) => 'y', chr(195).chr(190) => 'th',
|
||||
chr(195).chr(191) => 'y', chr(195).chr(152) => 'O',
|
||||
// Decompositions for Latin Extended-A
|
||||
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
|
||||
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
|
||||
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
|
||||
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
|
||||
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
|
||||
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
|
||||
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
|
||||
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
|
||||
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
|
||||
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
|
||||
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
|
||||
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
|
||||
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
|
||||
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
|
||||
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
|
||||
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
|
||||
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
|
||||
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
|
||||
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
|
||||
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
|
||||
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
|
||||
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
|
||||
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
|
||||
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
|
||||
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
|
||||
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
|
||||
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
|
||||
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
|
||||
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
|
||||
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
|
||||
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
|
||||
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
|
||||
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
|
||||
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
|
||||
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
|
||||
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
|
||||
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
|
||||
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
|
||||
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
|
||||
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
|
||||
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
|
||||
chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
|
||||
chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
|
||||
chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
|
||||
chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
|
||||
chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
|
||||
chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
|
||||
chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
|
||||
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
|
||||
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
|
||||
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
|
||||
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
|
||||
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
|
||||
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
|
||||
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
|
||||
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
|
||||
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
|
||||
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
|
||||
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
|
||||
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
|
||||
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
|
||||
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
|
||||
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
|
||||
chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
|
||||
// Decompositions for Latin Extended-B
|
||||
chr(200).chr(152) => 'S', chr(200).chr(153) => 's',
|
||||
chr(200).chr(154) => 'T', chr(200).chr(155) => 't',
|
||||
// Euro Sign
|
||||
chr(226).chr(130).chr(172) => 'E',
|
||||
// GBP (Pound) Sign
|
||||
chr(194).chr(163) => '',
|
||||
// Vowels with diacritic (Vietnamese)
|
||||
// unmarked
|
||||
chr(198).chr(160) => 'O', chr(198).chr(161) => 'o',
|
||||
chr(198).chr(175) => 'U', chr(198).chr(176) => 'u',
|
||||
// grave accent
|
||||
chr(225).chr(186).chr(166) => 'A', chr(225).chr(186).chr(167) => 'a',
|
||||
chr(225).chr(186).chr(176) => 'A', chr(225).chr(186).chr(177) => 'a',
|
||||
chr(225).chr(187).chr(128) => 'E', chr(225).chr(187).chr(129) => 'e',
|
||||
chr(225).chr(187).chr(146) => 'O', chr(225).chr(187).chr(147) => 'o',
|
||||
chr(225).chr(187).chr(156) => 'O', chr(225).chr(187).chr(157) => 'o',
|
||||
chr(225).chr(187).chr(170) => 'U', chr(225).chr(187).chr(171) => 'u',
|
||||
chr(225).chr(187).chr(178) => 'Y', chr(225).chr(187).chr(179) => 'y',
|
||||
// hook
|
||||
chr(225).chr(186).chr(162) => 'A', chr(225).chr(186).chr(163) => 'a',
|
||||
chr(225).chr(186).chr(168) => 'A', chr(225).chr(186).chr(169) => 'a',
|
||||
chr(225).chr(186).chr(178) => 'A', chr(225).chr(186).chr(179) => 'a',
|
||||
chr(225).chr(186).chr(186) => 'E', chr(225).chr(186).chr(187) => 'e',
|
||||
chr(225).chr(187).chr(130) => 'E', chr(225).chr(187).chr(131) => 'e',
|
||||
chr(225).chr(187).chr(136) => 'I', chr(225).chr(187).chr(137) => 'i',
|
||||
chr(225).chr(187).chr(142) => 'O', chr(225).chr(187).chr(143) => 'o',
|
||||
chr(225).chr(187).chr(148) => 'O', chr(225).chr(187).chr(149) => 'o',
|
||||
chr(225).chr(187).chr(158) => 'O', chr(225).chr(187).chr(159) => 'o',
|
||||
chr(225).chr(187).chr(166) => 'U', chr(225).chr(187).chr(167) => 'u',
|
||||
chr(225).chr(187).chr(172) => 'U', chr(225).chr(187).chr(173) => 'u',
|
||||
chr(225).chr(187).chr(182) => 'Y', chr(225).chr(187).chr(183) => 'y',
|
||||
// tilde
|
||||
chr(225).chr(186).chr(170) => 'A', chr(225).chr(186).chr(171) => 'a',
|
||||
chr(225).chr(186).chr(180) => 'A', chr(225).chr(186).chr(181) => 'a',
|
||||
chr(225).chr(186).chr(188) => 'E', chr(225).chr(186).chr(189) => 'e',
|
||||
chr(225).chr(187).chr(132) => 'E', chr(225).chr(187).chr(133) => 'e',
|
||||
chr(225).chr(187).chr(150) => 'O', chr(225).chr(187).chr(151) => 'o',
|
||||
chr(225).chr(187).chr(160) => 'O', chr(225).chr(187).chr(161) => 'o',
|
||||
chr(225).chr(187).chr(174) => 'U', chr(225).chr(187).chr(175) => 'u',
|
||||
chr(225).chr(187).chr(184) => 'Y', chr(225).chr(187).chr(185) => 'y',
|
||||
// acute accent
|
||||
chr(225).chr(186).chr(164) => 'A', chr(225).chr(186).chr(165) => 'a',
|
||||
chr(225).chr(186).chr(174) => 'A', chr(225).chr(186).chr(175) => 'a',
|
||||
chr(225).chr(186).chr(190) => 'E', chr(225).chr(186).chr(191) => 'e',
|
||||
chr(225).chr(187).chr(144) => 'O', chr(225).chr(187).chr(145) => 'o',
|
||||
chr(225).chr(187).chr(154) => 'O', chr(225).chr(187).chr(155) => 'o',
|
||||
chr(225).chr(187).chr(168) => 'U', chr(225).chr(187).chr(169) => 'u',
|
||||
// dot below
|
||||
chr(225).chr(186).chr(160) => 'A', chr(225).chr(186).chr(161) => 'a',
|
||||
chr(225).chr(186).chr(172) => 'A', chr(225).chr(186).chr(173) => 'a',
|
||||
chr(225).chr(186).chr(182) => 'A', chr(225).chr(186).chr(183) => 'a',
|
||||
chr(225).chr(186).chr(184) => 'E', chr(225).chr(186).chr(185) => 'e',
|
||||
chr(225).chr(187).chr(134) => 'E', chr(225).chr(187).chr(135) => 'e',
|
||||
chr(225).chr(187).chr(138) => 'I', chr(225).chr(187).chr(139) => 'i',
|
||||
chr(225).chr(187).chr(140) => 'O', chr(225).chr(187).chr(141) => 'o',
|
||||
chr(225).chr(187).chr(152) => 'O', chr(225).chr(187).chr(153) => 'o',
|
||||
chr(225).chr(187).chr(162) => 'O', chr(225).chr(187).chr(163) => 'o',
|
||||
chr(225).chr(187).chr(164) => 'U', chr(225).chr(187).chr(165) => 'u',
|
||||
chr(225).chr(187).chr(176) => 'U', chr(225).chr(187).chr(177) => 'u',
|
||||
chr(225).chr(187).chr(180) => 'Y', chr(225).chr(187).chr(181) => 'y',
|
||||
// Vowels with diacritic (Chinese, Hanyu Pinyin)
|
||||
chr(201).chr(145) => 'a',
|
||||
// macron
|
||||
chr(199).chr(149) => 'U', chr(199).chr(150) => 'u',
|
||||
// acute accent
|
||||
chr(199).chr(151) => 'U', chr(199).chr(152) => 'u',
|
||||
// caron
|
||||
chr(199).chr(141) => 'A', chr(199).chr(142) => 'a',
|
||||
chr(199).chr(143) => 'I', chr(199).chr(144) => 'i',
|
||||
chr(199).chr(145) => 'O', chr(199).chr(146) => 'o',
|
||||
chr(199).chr(147) => 'U', chr(199).chr(148) => 'u',
|
||||
chr(199).chr(153) => 'U', chr(199).chr(154) => 'u',
|
||||
// grave accent
|
||||
chr(199).chr(155) => 'U', chr(199).chr(156) => 'u',
|
||||
);
|
||||
|
||||
/*
|
||||
// Used for locale-specific rules
|
||||
$locale = get_locale();
|
||||
|
||||
if ( 'de_DE' == $locale ) {
|
||||
$chars[ chr(195).chr(132) ] = 'Ae';
|
||||
$chars[ chr(195).chr(164) ] = 'ae';
|
||||
$chars[ chr(195).chr(150) ] = 'Oe';
|
||||
$chars[ chr(195).chr(182) ] = 'oe';
|
||||
$chars[ chr(195).chr(156) ] = 'Ue';
|
||||
$chars[ chr(195).chr(188) ] = 'ue';
|
||||
$chars[ chr(195).chr(159) ] = 'ss';
|
||||
} elseif ( 'da_DK' === $locale ) {
|
||||
$chars[ chr(195).chr(134) ] = 'Ae';
|
||||
$chars[ chr(195).chr(166) ] = 'ae';
|
||||
$chars[ chr(195).chr(152) ] = 'Oe';
|
||||
$chars[ chr(195).chr(184) ] = 'oe';
|
||||
$chars[ chr(195).chr(133) ] = 'Aa';
|
||||
$chars[ chr(195).chr(165) ] = 'aa';
|
||||
}
|
||||
*/
|
||||
$string = strtr($string, $chars);
|
||||
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
134
inc/Translator.php
Normal file
134
inc/Translator.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace Franzz\Objects;
|
||||
use \Settings;
|
||||
|
||||
/**
|
||||
* Translator Class
|
||||
* @author franzz
|
||||
* @version 1.1
|
||||
*/
|
||||
class Translator extends PhpObject
|
||||
{
|
||||
private $sLang;
|
||||
private $sDefaultLang;
|
||||
private $asLanguages;
|
||||
private $asTranslations; // [lang][key_word] = translation
|
||||
|
||||
const LANG_FOLDER = 'languages/';
|
||||
const LANG_EXT = '.lang';
|
||||
const LANG_SEP = '=';
|
||||
const DEFAULT_LANG = 'en';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $sLang leave empty for auto selection based on $_SERVER['HTTP_ACCEPT_LANGUAGE']
|
||||
* @param string $sDefaultLang Fallback language if no fitting language is available
|
||||
*/
|
||||
public function __construct($sLang='', $sDefaultLang='')
|
||||
{
|
||||
parent::__construct(__FILE__, Settings::DEBUG);
|
||||
$this->asLanguages = array();
|
||||
$this->asTranslations = array();
|
||||
$this->loadLanguages();
|
||||
$this->setLanguage($sLang, $sDefaultLang);
|
||||
}
|
||||
|
||||
public function setLanguage($sLang, $sDefaultLang='') {
|
||||
$this->sDefaultLang = ($sDefaultLang=='')?self::DEFAULT_LANG:$sDefaultLang;
|
||||
$this->sLang = ($sLang=='')?ToolBox::getUserLanguage($this->asLanguages, $this->sDefaultLang):$sLang;
|
||||
}
|
||||
|
||||
public function getLanguage() {
|
||||
return $this->sLang;
|
||||
}
|
||||
|
||||
public function getTranslation($sTransKey='', $asParams=array(), $sLang='')
|
||||
{
|
||||
$oTransText = false;
|
||||
|
||||
//Select language & Scope
|
||||
if($sLang=='') $sLang = $this->sLang;
|
||||
$bAllTrans = ($sTransKey=='');
|
||||
|
||||
//Look up in the selected language dictionary
|
||||
if(in_array($sLang, $this->asLanguages) && ($bAllTrans || array_key_exists($sTransKey, $this->asTranslations[$sLang]))) {
|
||||
if($bAllTrans) $oTransText = array_merge($this->asTranslations[$this->sDefaultLang], $this->asTranslations[$sLang]);
|
||||
else $oTransText = $this->asTranslations[$sLang][$sTransKey];
|
||||
}
|
||||
//Look up in the default language dictionary
|
||||
elseif(!$bAllTrans && array_key_exists($sTransKey, $this->asTranslations[$this->sDefaultLang])) {
|
||||
$this->addWarning('Missing translation in "'.$sLang.'" for the key "'.$sTransKey.'", falling back to "'.$this->sDefaultLang.'"');
|
||||
$oTransText = $this->asTranslations[$this->sDefaultLang][$sTransKey];
|
||||
}
|
||||
else $this->addWarning($bAllTrans?'Missing language "'.$sLang.'"':'Missing translation in "'.$sLang.'" for the key "'.$sTransKey.'"');
|
||||
|
||||
if(is_string($asParams)) $asParams = array($asParams);
|
||||
if($oTransText && !$bAllTrans && !empty($asParams)) {
|
||||
foreach($asParams as $iIndex=>$sParam) $oTransText = str_replace('$'.$iIndex, $sParam, $oTransText);
|
||||
}
|
||||
|
||||
return $oTransText;
|
||||
}
|
||||
|
||||
public function getTranslations($sLang='') {
|
||||
return $this->getTranslation('', $sLang);
|
||||
}
|
||||
|
||||
public function getHashToPage($asMenuPages)
|
||||
{
|
||||
$asHashToPage = array();
|
||||
foreach($asMenuPages as $sHash)
|
||||
{
|
||||
foreach($this->asLanguages as $sLang)
|
||||
{
|
||||
$asHashToPage[$this->getTranslation('menu_'.$sHash.'_key', $sLang)] = $sHash;
|
||||
}
|
||||
}
|
||||
return $asHashToPage;
|
||||
}
|
||||
|
||||
public function getPageToHash($asMenuPages)
|
||||
{
|
||||
$asPageToHash = array();
|
||||
foreach($asMenuPages as $sHash)
|
||||
{
|
||||
$asPageToHash[$sHash] = $this->getTranslation('menu_'.$sHash.'_key');
|
||||
}
|
||||
return $asPageToHash;
|
||||
}
|
||||
|
||||
private function loadLanguages()
|
||||
{
|
||||
//List all available languages
|
||||
$asLangPaths = glob(self::getLangPath('*'));
|
||||
$this->asLanguages = array_map('basename', $asLangPaths, array_fill(1, count($asLangPaths), self::LANG_EXT));
|
||||
|
||||
//Load languages
|
||||
array_walk($this->asLanguages, array($this, 'loadLanguageFile'));
|
||||
}
|
||||
|
||||
private function loadLanguageFile($sLang)
|
||||
{
|
||||
if(!in_array($sLang, $this->asTranslations))
|
||||
{
|
||||
$sData = file_get_contents(self::getLangPath($sLang));
|
||||
$asData = explode("\n", $sData);
|
||||
foreach($asData as $sTranslation)
|
||||
{
|
||||
$iSepPos = mb_stripos($sTranslation, self::LANG_SEP);
|
||||
if($iSepPos !== false)
|
||||
{
|
||||
$sTransKey = trim(mb_substr($sTranslation, 0, $iSepPos));
|
||||
$sTransText = trim(mb_substr($sTranslation, $iSepPos+1));
|
||||
$this->asTranslations[$sLang][$sTransKey] = $sTransText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function getLangPath($sLang)
|
||||
{
|
||||
return self::LANG_FOLDER.$sLang.self::LANG_EXT;
|
||||
}
|
||||
}
|
||||
1483
inc/UploadHandler.php
Normal file
1483
inc/UploadHandler.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user