Release note v15.3
------------------ ### Fixes * Fix issue: classes should be loaded even the module is disabled * Deleting the only Admin user is not allowed * Fixes for handling non UTF-8 * Fix for non-mandatory select boxes are shown as mandatory
This commit is contained in:
@@ -9,9 +9,9 @@ define('HOME_LINK_ADMIN', CLIENT_BASE_URL."?g=admin&n=dashboard&m=admin_Admin");
|
||||
define('HOME_LINK_OTHERS', CLIENT_BASE_URL."?g=modules&n=dashboard&m=module_Personal_Information");
|
||||
|
||||
//Version
|
||||
define('VERSION', '15.2.OS');
|
||||
define('CACHE_VALUE', '15.2.OS');
|
||||
define('VERSION_DATE', '12/03/2016');
|
||||
define('VERSION', '15.3.OS');
|
||||
define('CACHE_VALUE', '15.3.OS');
|
||||
define('VERSION_DATE', '14/04/2016');
|
||||
|
||||
if(!defined('CONTACT_EMAIL')){define('CONTACT_EMAIL','icehrm@gamonoid.com');}
|
||||
if(!defined('KEY_PREFIX')){define('KEY_PREFIX','IceHrm');}
|
||||
|
||||
@@ -61,9 +61,13 @@ class ICEHRM_Record extends ADOdb_Active_Record{
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function getDefaultAccessLevel(){
|
||||
return array("get","element","save","delete");
|
||||
}
|
||||
public function postProcessGetElement($obj){
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function getDefaultAccessLevel(){
|
||||
return array("get","element","save","delete");
|
||||
}
|
||||
|
||||
public function getVirtualFields(){
|
||||
return array(
|
||||
|
||||
@@ -532,6 +532,14 @@ That way you can attach each and every project to a client.
|
||||
Under employee projects tab you can assign projects to employees. You need to add projects to employees to enable them to add time against
|
||||
these projects in time-sheets.
|
||||
|
||||
Release note v15.3
|
||||
------------------
|
||||
### Fixes
|
||||
* Fix issue: classes should be loaded even the module is disabled
|
||||
* Deleting the only Admin user is not allowed
|
||||
* Fixes for handling non UTF-8
|
||||
* Fix for non-mandatory select boxes are shown as mandatory
|
||||
|
||||
Release note v15.2
|
||||
------------------
|
||||
|
||||
|
||||
@@ -680,6 +680,299 @@ ApproveModuleAdapter.method('getActionButtonsHtml', function(id,data) {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TableEditAdapter
|
||||
*/
|
||||
|
||||
function TableEditAdapter(endPoint) {
|
||||
this.initAdapter(endPoint);
|
||||
this.cellDataUpdates = {};
|
||||
this.modulePath = '';
|
||||
this.rowFieldName = '';
|
||||
this.columnFieldName = '';
|
||||
this.rowTable = '';
|
||||
this.columnTable = '';
|
||||
this.valueTable = '';
|
||||
this.csvData = [];
|
||||
}
|
||||
|
||||
TableEditAdapter.inherits(AdapterBase);
|
||||
|
||||
TableEditAdapter.method('setModulePath', function(path) {
|
||||
this.modulePath = path;
|
||||
});
|
||||
|
||||
TableEditAdapter.method('setRowFieldName', function(name) {
|
||||
this.rowFieldName = name;
|
||||
});
|
||||
|
||||
TableEditAdapter.method('setTables', function(rowTable, columnTable, valueTable) {
|
||||
this.rowTable = rowTable;
|
||||
this.columnTable = columnTable;
|
||||
this.valueTable = valueTable;
|
||||
});
|
||||
|
||||
TableEditAdapter.method('setColumnFieldName', function(name) {
|
||||
this.columnFieldName = name;
|
||||
});
|
||||
|
||||
TableEditAdapter.method('getDataMapping', function() {
|
||||
return [
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
TableEditAdapter.method('getFormFields', function() {
|
||||
return [
|
||||
];
|
||||
});
|
||||
|
||||
TableEditAdapter.method('get', function() {
|
||||
this.getAllData();
|
||||
});
|
||||
|
||||
TableEditAdapter.method('getAllData', function(save) {
|
||||
var req = {};
|
||||
req.rowTable = this.rowTable;
|
||||
req.columnTable = this.columnTable;
|
||||
req.valueTable = this.valueTable;
|
||||
req = this.addAdditionalRequestData('getAllData', req);
|
||||
req.save = (save == undefined || save == null || save == false)?0:1;
|
||||
var reqJson = JSON.stringify(req);
|
||||
|
||||
var callBackData = [];
|
||||
callBackData['callBackData'] = [];
|
||||
callBackData['callBackSuccess'] = 'getAllDataSuccessCallBack';
|
||||
callBackData['callBackFail'] = 'getAllDataFailCallBack';
|
||||
|
||||
this.customAction('getAllData',this.modulePath,reqJson,callBackData);
|
||||
});
|
||||
|
||||
TableEditAdapter.method('getDataItem', function(row,column,allData) {
|
||||
var columnData = allData[1];
|
||||
var rowData = allData[0];
|
||||
var serverData = allData[2];
|
||||
|
||||
if(column == -1){
|
||||
return rowData[row].name;
|
||||
}else{
|
||||
return this.getDataItemByKeyValues(this.rowFieldName, rowData[row].id, this.columnFieldName, columnData[column].id, serverData);
|
||||
}
|
||||
});
|
||||
|
||||
TableEditAdapter.method('getDataItemByKeyValues', function(rowKeyName, rowKeyVal, colKeyName, colKeyVal, data) {
|
||||
for(var i=0;i<data.length;i++){
|
||||
if(data[i][rowKeyName] == rowKeyVal && data[i][colKeyName] == colKeyVal){
|
||||
return (data[i].amount != undefined && data[i].amount != null)?data[i].amount:"";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
TableEditAdapter.method('getAllDataSuccessCallBack', function(allData) {
|
||||
|
||||
var serverData = allData[2];
|
||||
var columnData = allData[1];
|
||||
var rowData = allData[0];
|
||||
var data = [];
|
||||
for(var i=0;i<rowData.length;i++){
|
||||
var row = [];
|
||||
for(var j=-1;j<columnData.length;j++){
|
||||
row[j+1] = this.getDataItem(i, j,allData);
|
||||
}
|
||||
data.push(this.preProcessTableData(row));
|
||||
}
|
||||
this.sourceData = serverData;
|
||||
|
||||
|
||||
this.tableData = data;
|
||||
this.setHeaders(columnData,rowData);
|
||||
this.createTable(this.getTableName());
|
||||
$("#"+this.getTableName()+'Form').hide();
|
||||
$("#"+this.getTableName()).show();
|
||||
|
||||
this.csvData = [];
|
||||
|
||||
var tmpRow = [];
|
||||
for(var i=0;i<columnData.length;i++){
|
||||
tmpRow.push(columnData[i].name);
|
||||
}
|
||||
tmpRow = this.modifyCSVHeader(tmpRow);
|
||||
this.csvData.push(tmpRow);
|
||||
|
||||
for(var i=0; i<data.length; i++){
|
||||
this.csvData.push(data[i]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
TableEditAdapter.method('modifyCSVHeader', function(header) {
|
||||
return header;
|
||||
});
|
||||
|
||||
TableEditAdapter.method('getAllDataFailCallBack', function(callBackData,serverData) {
|
||||
|
||||
});
|
||||
|
||||
TableEditAdapter.method('setHeaders', function(columns, rows) {
|
||||
var headers = [];
|
||||
headers.push({ "sTitle": "", sWidth:"180px;"});
|
||||
var sclass = "";
|
||||
for(var i=0;i<columns.length;i++){
|
||||
|
||||
if(columns[i].editable == undefined || columns[i].editable == null || columns[i].editable == 'Yes'){
|
||||
sclass = "editcell";
|
||||
}else{
|
||||
sclass = "";
|
||||
}
|
||||
headers.push({ "sTitle": columns[i].name,sClass: sclass,
|
||||
"fnCreatedCell": function (nTd, sData, oData, iRow, iCol) {
|
||||
$(nTd).data('colId',columns[iCol-1].id);
|
||||
$(nTd).data('rowId',rows[iRow].id);
|
||||
|
||||
}});
|
||||
}
|
||||
|
||||
this.headers = headers;
|
||||
});
|
||||
|
||||
TableEditAdapter.method('getHeaders', function() {
|
||||
return this.headers;
|
||||
});
|
||||
|
||||
TableEditAdapter.method('createTable', function(elementId) {
|
||||
|
||||
var data = this.getTableData();
|
||||
var headers = this.getHeaders();
|
||||
|
||||
if(this.showActionButtons()){
|
||||
headers.push({ "sTitle": "", "sClass": "center" });
|
||||
}
|
||||
|
||||
|
||||
if(this.showActionButtons()){
|
||||
for(var i=0;i<data.length;i++){
|
||||
data[i].push(this.getActionButtonsHtml(data[i][0],data[i]));
|
||||
}
|
||||
}
|
||||
var html = "";
|
||||
html = this.getTableTopButtonHtml()+'<div class="box-body table-responsive"><table cellpadding="0" cellspacing="0" border="0" class="table table-bordered table-striped" id="grid"></table></div>';
|
||||
|
||||
//Find current page
|
||||
var activePage = $('#'+elementId +" .dataTables_paginate .active a").html();
|
||||
var start = 0;
|
||||
if(activePage != undefined && activePage != null){
|
||||
start = parseInt(activePage, 10)*15 - 15;
|
||||
}
|
||||
|
||||
$('#'+elementId).html(html);
|
||||
|
||||
var dataTableParams = {
|
||||
"oLanguage": {
|
||||
"sLengthMenu": "_MENU_ records per page"
|
||||
},
|
||||
"aaData": data,
|
||||
"aoColumns": headers,
|
||||
"bSort": false,
|
||||
"iDisplayLength": 15,
|
||||
"iDisplayStart": start
|
||||
};
|
||||
|
||||
|
||||
var customTableParams = this.getCustomTableParams();
|
||||
|
||||
$.extend(dataTableParams, customTableParams);
|
||||
|
||||
$('#'+elementId+' #grid').dataTable( dataTableParams );
|
||||
|
||||
$(".dataTables_paginate ul").addClass("pagination");
|
||||
$(".dataTables_length").hide();
|
||||
$(".dataTables_filter input").addClass("form-control");
|
||||
$(".dataTables_filter input").attr("placeholder","Search");
|
||||
$(".dataTables_filter label").contents().filter(function(){
|
||||
return (this.nodeType == 3);
|
||||
}).remove();
|
||||
//$('.tableActionButton').tooltip();
|
||||
$('#'+elementId+' #grid').editableTableWidget();
|
||||
|
||||
$('#'+elementId+' #grid .editcell').on('validate', function(evt, newValue) {
|
||||
|
||||
return modJs.validateCellValue($(this), evt, newValue);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
TableEditAdapter.method('addCellDataUpdate' , function(colId, rowId, data) {
|
||||
|
||||
this.cellDataUpdates[colId+"="+rowId] = [colId, rowId, data];
|
||||
});
|
||||
|
||||
TableEditAdapter.method('addAdditionalRequestData' , function(type, req) {
|
||||
return req;
|
||||
});
|
||||
|
||||
TableEditAdapter.method('sendCellDataUpdates' , function() {
|
||||
var req = this.cellDataUpdates;
|
||||
req.rowTable = this.rowTable;
|
||||
req.columnTable = this.columnTable;
|
||||
req.valueTable = this.valueTable;
|
||||
req = this.addAdditionalRequestData('updateData', req);
|
||||
var reqJson = JSON.stringify(req);
|
||||
|
||||
var callBackData = [];
|
||||
callBackData['callBackData'] = [];
|
||||
callBackData['callBackSuccess'] = 'updateDataSuccessCallBack';
|
||||
callBackData['callBackFail'] = 'updateDataFailCallBack';
|
||||
this.showLoader();
|
||||
this.customAction('updateData',this.modulePath,reqJson,callBackData);
|
||||
});
|
||||
|
||||
TableEditAdapter.method('updateDataSuccessCallBack', function(callBackData,serverData) {
|
||||
this.hideLoader();
|
||||
modJs.cellDataUpdates = {};
|
||||
modJs.get();
|
||||
});
|
||||
|
||||
TableEditAdapter.method('updateDataFailCallBack', function(callBackData,serverData) {
|
||||
this.hideLoader();
|
||||
});
|
||||
|
||||
TableEditAdapter.method('sendAllCellDataUpdates' , function() {
|
||||
|
||||
var req = this.cellDataUpdates;
|
||||
req.rowTable = this.rowTable;
|
||||
req.columnTable = this.columnTable;
|
||||
req.valueTable = this.valueTable;
|
||||
req = this.addAdditionalRequestData('updateAllData', req);
|
||||
var reqJson = JSON.stringify(req);
|
||||
|
||||
var callBackData = [];
|
||||
callBackData['callBackData'] = [];
|
||||
callBackData['callBackSuccess'] = 'updateDataAllSuccessCallBack';
|
||||
callBackData['callBackFail'] = 'updateDataAllFailCallBack';
|
||||
this.showLoader();
|
||||
this.customAction('updateAllData',this.modulePath,reqJson,callBackData);
|
||||
});
|
||||
|
||||
TableEditAdapter.method('updateDataAllSuccessCallBack', function(callBackData,serverData) {
|
||||
this.hideLoader();
|
||||
modJs.cellDataUpdates = {};
|
||||
modJs.getAllData(true);
|
||||
});
|
||||
|
||||
TableEditAdapter.method('updateDataAllFailCallBack', function(callBackData,serverData) {
|
||||
this.hideLoader();
|
||||
});
|
||||
|
||||
TableEditAdapter.method('showActionButtons' , function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* RequestCache
|
||||
|
||||
@@ -1725,7 +1725,10 @@ IceHRMBase.method('fillForm', function(object, formId, fields) {
|
||||
}
|
||||
|
||||
$(formId + ' #'+fields[i][0]).select2('val',msVal);
|
||||
|
||||
var select2Height = $(formId + ' #'+fields[i][0]).find(".select2-choices").height();
|
||||
$(formId + ' #'+fields[i][0]).find(".controls").css('min-height', select2Height+"px");
|
||||
$(formId + ' #'+fields[i][0]).css('min-height', select2Height+"px");
|
||||
|
||||
}else if(fields[i][1].type == 'datagroup'){
|
||||
try{
|
||||
var html = this.dataGroupToHtml(object[fields[i][0]],fields[i]);
|
||||
@@ -1764,7 +1767,13 @@ IceHRMBase.method('renderFormField', function(field) {
|
||||
}
|
||||
var t = this.fieldTemplates[field[1].type];
|
||||
if(field[1].validation != "none" && field[1].validation != "emailOrEmpty" && field[1].validation != "numberOrEmpty" && field[1].type != "placeholder" && field[1].label.indexOf('*') < 0){
|
||||
field[1].label = field[1].label + '<font class="redFont">*</font>';
|
||||
var tempSelectBoxes = ['select','select2'];
|
||||
if(tempSelectBoxes.indexOf(field[1].type) >= 0 && field[1]['allow-null'] == true){
|
||||
|
||||
}else{
|
||||
field[1].label = field[1].label + '<font class="redFont">*</font>';
|
||||
}
|
||||
|
||||
}
|
||||
if(field[1].type == 'text' || field[1].type == 'textarea' || field[1].type == 'hidden' || field[1].type == 'label' || field[1].type == 'placeholder'){
|
||||
t = t.replace(/_id_/g,field[0]);
|
||||
|
||||
@@ -65,6 +65,10 @@ abstract class AbstractModuleManager{
|
||||
*/
|
||||
public abstract function setupModuleClassDefinitions();
|
||||
|
||||
public function initCalculationHooks(){
|
||||
|
||||
}
|
||||
|
||||
public function initQuickAccessMenu(){
|
||||
|
||||
}
|
||||
@@ -207,6 +211,10 @@ abstract class AbstractModuleManager{
|
||||
|
||||
$eh->Save();
|
||||
}
|
||||
|
||||
public function addCalculationHook($code, $name, $class, $method){
|
||||
BaseService::getInstance()->addCalculationHook($code, $name, $class, $method);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -46,7 +46,8 @@ class BaseService{
|
||||
var $emailSender = null;
|
||||
var $user = null;
|
||||
var $historyManagers = array();
|
||||
|
||||
var $calculationHooks = array();
|
||||
|
||||
private static $me = null;
|
||||
|
||||
private function __construct(){
|
||||
@@ -131,10 +132,15 @@ class BaseService{
|
||||
LogManager::getInstance()->debug("Query: "."1=1".$query.$orderBy);
|
||||
LogManager::getInstance()->debug("Query Data: ".print_r($queryData,true));
|
||||
$list = $obj->Find("1=1".$query.$orderBy,$queryData);
|
||||
}
|
||||
}
|
||||
|
||||
$newList = array();
|
||||
foreach($list as $listObj){
|
||||
$newList[] = $this->cleanUpAdoDB($listObj);
|
||||
}
|
||||
|
||||
if(!empty($mappingStr) && count($map)>0){
|
||||
$list = $this->populateMapping($list, $map);
|
||||
$list = $this->populateMapping($newList, $map);
|
||||
}
|
||||
|
||||
return $list;
|
||||
@@ -363,7 +369,7 @@ class BaseService{
|
||||
|
||||
$processedList = array();
|
||||
foreach($list as $obj){
|
||||
$processedList[] = $obj->postProcessGetData($obj);
|
||||
$processedList[] = $this->cleanUpAdoDB($obj->postProcessGetData($obj));
|
||||
}
|
||||
|
||||
$list = $processedList;
|
||||
@@ -488,7 +494,8 @@ class BaseService{
|
||||
}
|
||||
}
|
||||
}
|
||||
return $obj->postProcessGetData($obj);
|
||||
$obj = $obj->postProcessGetElement($obj);
|
||||
return $this->cleanUpAdoDB($obj->postProcessGetData($obj));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -685,6 +692,7 @@ class BaseService{
|
||||
}
|
||||
|
||||
foreach($list as $obj){
|
||||
$obj = $this->cleanUpAdoDB($obj);
|
||||
if(count($values) == 1){
|
||||
$ret[$obj->$key] = $obj->$value;
|
||||
}else{
|
||||
@@ -1258,6 +1266,60 @@ class BaseService{
|
||||
|
||||
return $obj;
|
||||
|
||||
}
|
||||
|
||||
public function addCalculationHook($code, $name, $class, $method){
|
||||
$calcualtionHook = new CalculationHook();
|
||||
$calcualtionHook->code = $code;
|
||||
$calcualtionHook->name = $name;
|
||||
$calcualtionHook->class = $class;
|
||||
$calcualtionHook->method = $method;
|
||||
$this->calculationHooks[$code] = $calcualtionHook;
|
||||
}
|
||||
|
||||
public function getCalculationHooks(){
|
||||
return array_values($this->calculationHooks);
|
||||
}
|
||||
|
||||
public function getCalculationHook($code){
|
||||
return $this->calculationHooks[$code];
|
||||
}
|
||||
|
||||
public function executeCalculationHook($parameters, $code = NULL){
|
||||
$ch = BaseService::getInstance()->getCalculationHook($code);
|
||||
|
||||
if(empty($ch->code)){
|
||||
return null;
|
||||
}
|
||||
$class = $ch->class;
|
||||
return call_user_func_array(array(new $class(), $ch->method), $parameters);
|
||||
}
|
||||
|
||||
public function cleanNonUTFChar($obj){
|
||||
$regex = <<<'END'
|
||||
/
|
||||
(
|
||||
(?: [\x00-\x7F] # single-byte sequences 0xxxxxxx
|
||||
| [\xC0-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
|
||||
| [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2
|
||||
| [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3
|
||||
){1,100} # ...one or more times
|
||||
)
|
||||
| . # anything else
|
||||
/x
|
||||
END;
|
||||
if(is_string($obj)){
|
||||
return preg_replace($regex, '$1', $obj);
|
||||
}else{
|
||||
|
||||
foreach($obj as $key => $val){
|
||||
|
||||
|
||||
$obj->$key = preg_replace($regex, '$1', $val);
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ class IceCron{
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
if(intval(date('m')) <= intval($time) && date('H') != date('H',strtotime($lastRunTime))){
|
||||
if(intval(date('i')) <= intval($time) && date('H') != date('H',strtotime($lastRunTime))){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ class IceCron{
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
if(intval(date('H')) <= intval($time) && date('d') != date('d',strtotime($lastRunTime))){
|
||||
if(intval(date('H')) >= intval($time) && date('d') != date('d',strtotime($lastRunTime))){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,7 @@ class IceCron{
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
if(intval(date('d')) <= intval($time) && date('m') != date('m',strtotime($lastRunTime))){
|
||||
if(intval(date('d')) >= intval($time) && date('m') != date('m',strtotime($lastRunTime))){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ class IceCron{
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
if(intval(date('m')) <= intval($time) && date('Y') != date('Y',strtotime($lastRunTime))){
|
||||
if(intval(date('m')) >= intval($time) && date('Y') != date('Y',strtotime($lastRunTime))){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
176
src/classes/Macaw.php
Executable file
176
src/classes/Macaw.php
Executable file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace NoahBuscher\Macaw;
|
||||
|
||||
/**
|
||||
* @method static Macaw get(string $route, Callable $callback)
|
||||
* @method static Macaw post(string $route, Callable $callback)
|
||||
* @method static Macaw put(string $route, Callable $callback)
|
||||
* @method static Macaw delete(string $route, Callable $callback)
|
||||
* @method static Macaw options(string $route, Callable $callback)
|
||||
* @method static Macaw head(string $route, Callable $callback)
|
||||
*/
|
||||
class Macaw
|
||||
{
|
||||
|
||||
public static $halts = false;
|
||||
|
||||
public static $routes = array();
|
||||
|
||||
public static $methods = array();
|
||||
|
||||
public static $callbacks = array();
|
||||
|
||||
public static $patterns = array(
|
||||
':any' => '[^/]+',
|
||||
':num' => '[0-9]+',
|
||||
':all' => '.*'
|
||||
);
|
||||
|
||||
public static $error_callback;
|
||||
|
||||
/**
|
||||
* Defines a route w/ callback and method
|
||||
*/
|
||||
public static function __callstatic($method, $params)
|
||||
{
|
||||
|
||||
$uri = dirname($_SERVER['PHP_SELF']).$params[0];
|
||||
$callback = $params[1];
|
||||
|
||||
array_push(self::$routes, $uri);
|
||||
array_push(self::$methods, strtoupper($method));
|
||||
array_push(self::$callbacks, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines callback if route is not found
|
||||
*/
|
||||
public static function error($callback)
|
||||
{
|
||||
self::$error_callback = $callback;
|
||||
}
|
||||
|
||||
public static function haltOnMatch($flag = true)
|
||||
{
|
||||
self::$halts = $flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the callback for the given request
|
||||
*/
|
||||
public static function dispatch()
|
||||
{
|
||||
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
$searches = array_keys(static::$patterns);
|
||||
$replaces = array_values(static::$patterns);
|
||||
|
||||
$found_route = false;
|
||||
|
||||
self::$routes = str_replace('//', '/', self::$routes);
|
||||
|
||||
// check if route is defined without regex
|
||||
if (in_array($uri, self::$routes)) {
|
||||
$route_pos = array_keys(self::$routes, $uri);
|
||||
foreach ($route_pos as $route) {
|
||||
|
||||
//using an ANY option to match both GET and POST requests
|
||||
if (self::$methods[$route] == $method || self::$methods[$route] == 'ANY') {
|
||||
$found_route = true;
|
||||
|
||||
//if route is not an object
|
||||
if(!is_object(self::$callbacks[$route])){
|
||||
|
||||
//grab all parts based on a / separator
|
||||
$parts = explode('/',self::$callbacks[$route]);
|
||||
|
||||
//collect the last index of the array
|
||||
$last = end($parts);
|
||||
|
||||
//grab the controller name and method call
|
||||
$segments = explode('@',$last);
|
||||
|
||||
//instanitate controller
|
||||
$controller = new $segments[0]();
|
||||
|
||||
//call method
|
||||
$controller->$segments[1]();
|
||||
|
||||
if (self::$halts) return;
|
||||
|
||||
} else {
|
||||
//call closure
|
||||
call_user_func(self::$callbacks[$route]);
|
||||
|
||||
if (self::$halts) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// check if defined with regex
|
||||
$pos = 0;
|
||||
foreach (self::$routes as $route) {
|
||||
|
||||
if (strpos($route, ':') !== false) {
|
||||
$route = str_replace($searches, $replaces, $route);
|
||||
}
|
||||
|
||||
if (preg_match('#^' . $route . '$#', $uri, $matched)) {
|
||||
if (self::$methods[$pos] == $method) {
|
||||
$found_route = true;
|
||||
|
||||
array_shift($matched); //remove $matched[0] as [1] is the first parameter.
|
||||
|
||||
|
||||
if(!is_object(self::$callbacks[$pos])){
|
||||
|
||||
//grab all parts based on a / separator
|
||||
$parts = explode('/',self::$callbacks[$pos]);
|
||||
|
||||
//collect the last index of the array
|
||||
$last = end($parts);
|
||||
|
||||
//grab the controller name and method call
|
||||
$segments = explode('@',$last);
|
||||
|
||||
//instanitate controller
|
||||
$controller = new $segments[0]();
|
||||
|
||||
//fix multi parameters
|
||||
if(!method_exists($controller, $segments[1])){
|
||||
echo "controller and action not found";
|
||||
}else{
|
||||
call_user_func_array(array($controller, $segments[1]), $matched);
|
||||
}
|
||||
|
||||
//call method and pass any extra parameters to the method
|
||||
// $controller->$segments[1](implode(",", $matched));
|
||||
|
||||
if (self::$halts) return;
|
||||
} else {
|
||||
call_user_func_array(self::$callbacks[$pos], $matched);
|
||||
|
||||
if (self::$halts) return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
$pos++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// run the error callback if the route was not found
|
||||
if ($found_route == false) {
|
||||
if (!self::$error_callback) {
|
||||
self::$error_callback = function() {
|
||||
header($_SERVER['SERVER_PROTOCOL']." 404 Not Found");
|
||||
echo '404';
|
||||
};
|
||||
}
|
||||
call_user_func(self::$error_callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,14 +49,14 @@ class RestApiManager{
|
||||
$accessToken = $this->generateUserAccessToken($user)->getData();
|
||||
if(!empty($accessTokenObj->id)){
|
||||
$accessTokenObj->token = $accessToken;
|
||||
$accessTokenObj->hash = base64_encode(CLIENT_BASE_URL).":".md5($accessTokenObj->token);
|
||||
$accessTokenObj->hash = md5(CLIENT_BASE_URL.$accessTokenObj->token);
|
||||
$accessTokenObj->updated = date("Y-m-d H:i:s");
|
||||
$accessTokenObj->Save();
|
||||
}else{
|
||||
$accessTokenObj = new RestAccessToken();
|
||||
$accessTokenObj->userId = $user->id;
|
||||
$accessTokenObj->token = $accessToken;
|
||||
$accessTokenObj->hash = base64_encode(CLIENT_BASE_URL).":".md5($accessTokenObj->token);
|
||||
$accessTokenObj->hash = md5(CLIENT_BASE_URL.$accessTokenObj->token);
|
||||
$accessTokenObj->updated = date("Y-m-d H:i:s");
|
||||
$accessTokenObj->created = date("Y-m-d H:i:s");
|
||||
$accessTokenObj->Save();
|
||||
@@ -69,13 +69,14 @@ class RestApiManager{
|
||||
|
||||
public function validateAccessToken($hash){
|
||||
$accessTokenObj = new RestAccessToken();
|
||||
LogManager::getInstance()->info("AT Hash:".$hash);
|
||||
$accessTokenObj->Load("hash = ?",array($hash));
|
||||
|
||||
LogManager::getInstance()->info("AT Hash Object:".json_encode($accessTokenObj));
|
||||
if(!empty($accessTokenObj->id) && $accessTokenObj->hash == $hash){
|
||||
return $this->validateAccessTokenInner($accessTokenObj->token);
|
||||
}
|
||||
|
||||
return new IceResponse(IceResponse::ERROR, "Acess Token not found");
|
||||
return new IceResponse(IceResponse::ERROR, "Access Token not found");
|
||||
}
|
||||
|
||||
private function validateAccessTokenInner($accessToken){
|
||||
@@ -122,30 +123,40 @@ class RestApiManager{
|
||||
|
||||
|
||||
class RestEndPoint{
|
||||
var $url;
|
||||
|
||||
public function setUrl($url){
|
||||
$this->url = $url;
|
||||
|
||||
public function process($type , $parameter = NULL){
|
||||
$resp = $this->$type($parameter);
|
||||
$this->printResponse($resp);
|
||||
}
|
||||
|
||||
public function getUrl(){
|
||||
return $this->url;
|
||||
public function get($parameter){
|
||||
return new IceResponse(IceResponse::ERROR, "Method not Implemented");
|
||||
}
|
||||
|
||||
public function get($parameters){
|
||||
return new IceResponse(IceResponse::ERROR, false);
|
||||
public function post($parameter){
|
||||
return new IceResponse(IceResponse::ERROR, "Method not Implemented");
|
||||
}
|
||||
|
||||
public function post($parameters){
|
||||
return new IceResponse(IceResponse::ERROR, false);
|
||||
public function put($parameter){
|
||||
return new IceResponse(IceResponse::ERROR, "Method not Implemented");
|
||||
}
|
||||
|
||||
public function put($parameters){
|
||||
return new IceResponse(IceResponse::ERROR, false);
|
||||
public function delete($parameter){
|
||||
return new IceResponse(IceResponse::ERROR, "Method not Implemented");
|
||||
}
|
||||
|
||||
public function delete($parameters){
|
||||
return new IceResponse(IceResponse::ERROR, false);
|
||||
|
||||
public function clearObject($obj){
|
||||
return BaseService::getInstance()->cleanUpAdoDB($obj);
|
||||
}
|
||||
|
||||
public function validateAccessToken(){
|
||||
$accessTokenValidation = RestApiManager::getInstance()->validateAccessToken($_REQUEST['access_token']);
|
||||
|
||||
return $accessTokenValidation;
|
||||
}
|
||||
|
||||
public function printResponse($response){
|
||||
echo json_encode($response,JSON_PRETTY_PRINT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
src/data.php
13
src/data.php
@@ -90,7 +90,18 @@ if(in_array($table, BaseService::getInstance()->userTables) && !$skipProfileRest
|
||||
}
|
||||
$subordinatesIds.=$sub->id;
|
||||
}
|
||||
$subordinatesIds.="";
|
||||
if($obj->allowIndirectMapping()){
|
||||
$indeirectEmployees = $subordinate->Find("indirect_supervisors IS NOT NULL and indirect_supervisors <> '' and status = 'Active'", array());
|
||||
foreach($indeirectEmployees as $ie){
|
||||
$indirectSupervisors = json_decode($ie->indirect_supervisors, true);
|
||||
if(in_array($cemp, $indirectSupervisors)){
|
||||
if($subordinatesIds != ""){
|
||||
$subordinatesIds.=",";
|
||||
}
|
||||
$subordinatesIds.=$ie->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sql = "Select count(id) as count from ".$obj->_table." where ".SIGN_IN_ELEMENT_MAPPING_FIELD_NAME." in (".$subordinatesIds.") ".$countFilterQuery;
|
||||
LogManager::getInstance()->debug("Count Filter Query 2:".$sql);
|
||||
LogManager::getInstance()->debug("Count Filter Query Data 2:".json_encode($countFilterQueryData));
|
||||
|
||||
@@ -12,6 +12,7 @@ include (APP_BASE_PATH."utils/SessionUtils.php");
|
||||
include (APP_BASE_PATH."utils/InputCleaner.php");
|
||||
include (APP_BASE_PATH."utils/LogManager.php");
|
||||
include (APP_BASE_PATH."utils/CalendarTools.php");
|
||||
include (APP_BASE_PATH."utils/EvalMath.php");
|
||||
|
||||
|
||||
$_REQUEST = InputCleaner::cleanParameters($_REQUEST);
|
||||
|
||||
132
src/js/mindmup-editabletable.js
Normal file
132
src/js/mindmup-editabletable.js
Normal file
@@ -0,0 +1,132 @@
|
||||
/*global $, window*/
|
||||
$.fn.editableTableWidget = function (options) {
|
||||
'use strict';
|
||||
return $(this).each(function () {
|
||||
var buildDefaultOptions = function () {
|
||||
var opts = $.extend({}, $.fn.editableTableWidget.defaultOptions);
|
||||
opts.editor = opts.editor.clone();
|
||||
return opts;
|
||||
},
|
||||
activeOptions = $.extend(buildDefaultOptions(), options),
|
||||
ARROW_LEFT = 37, ARROW_UP = 38, ARROW_RIGHT = 39, ARROW_DOWN = 40, ENTER = 13, ESC = 27, TAB = 9,
|
||||
element = $(this),
|
||||
editor = activeOptions.editor.css('position', 'absolute').hide().appendTo(element.parent()),
|
||||
active,
|
||||
showEditor = function (select) {
|
||||
//active = element.find('td:focus');
|
||||
active = element.find('td.editcell:focus');
|
||||
if (active.length) {
|
||||
editor.val(active.text())
|
||||
.removeClass('error')
|
||||
.show()
|
||||
.offset(active.offset())
|
||||
.css(active.css(activeOptions.cloneProperties))
|
||||
.width(active.width())
|
||||
.height(active.height())
|
||||
.focus();
|
||||
if (select) {
|
||||
editor.select();
|
||||
}
|
||||
}
|
||||
},
|
||||
setActiveText = function () {
|
||||
var text = editor.val(),
|
||||
evt = $.Event('change'),
|
||||
originalContent;
|
||||
if (active.text() === text || editor.hasClass('error')) {
|
||||
return true;
|
||||
}
|
||||
originalContent = active.html();
|
||||
active.text(text).trigger(evt, text);
|
||||
if (evt.result === false) {
|
||||
active.html(originalContent);
|
||||
}
|
||||
},
|
||||
movement = function (element, keycode) {
|
||||
if (keycode === ARROW_RIGHT) {
|
||||
return element.next('td');
|
||||
} else if (keycode === ARROW_LEFT) {
|
||||
return element.prev('td');
|
||||
} else if (keycode === ARROW_UP) {
|
||||
return element.parent().prev().children().eq(element.index());
|
||||
} else if (keycode === ARROW_DOWN) {
|
||||
return element.parent().next().children().eq(element.index());
|
||||
}
|
||||
return [];
|
||||
};
|
||||
editor.blur(function () {
|
||||
setActiveText();
|
||||
editor.hide();
|
||||
}).keydown(function (e) {
|
||||
if (e.which === ENTER) {
|
||||
setActiveText();
|
||||
editor.hide();
|
||||
active.focus();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
} else if (e.which === ESC) {
|
||||
editor.val(active.text());
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
editor.hide();
|
||||
active.focus();
|
||||
} else if (e.which === TAB) {
|
||||
active.focus();
|
||||
} else if (this.selectionEnd - this.selectionStart === this.value.length) {
|
||||
var possibleMove = movement(active, e.which);
|
||||
if (possibleMove.length > 0) {
|
||||
possibleMove.focus();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
})
|
||||
.on('input paste', function () {
|
||||
var evt = $.Event('validate');
|
||||
active.trigger(evt, editor.val());
|
||||
if (evt.result === false) {
|
||||
editor.addClass('error');
|
||||
} else {
|
||||
editor.removeClass('error');
|
||||
}
|
||||
});
|
||||
element.on('click keypress dblclick', showEditor)
|
||||
.css('cursor', 'pointer')
|
||||
.keydown(function (e) {
|
||||
var prevent = true,
|
||||
possibleMove = movement($(e.target), e.which);
|
||||
if (possibleMove.length > 0) {
|
||||
possibleMove.focus();
|
||||
} else if (e.which === ENTER) {
|
||||
showEditor(false);
|
||||
} else if (e.which === 17 || e.which === 91 || e.which === 93) {
|
||||
showEditor(true);
|
||||
prevent = false;
|
||||
} else {
|
||||
prevent = false;
|
||||
}
|
||||
if (prevent) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
element.find('td').prop('tabindex', 1);
|
||||
|
||||
$(window).on('resize', function () {
|
||||
if (editor.is(':visible')) {
|
||||
editor.offset(active.offset())
|
||||
.width(active.width())
|
||||
.height(active.height());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
$.fn.editableTableWidget.defaultOptions = {
|
||||
cloneProperties: ['padding', 'padding-top', 'padding-bottom', 'padding-left', 'padding-right',
|
||||
'text-align', 'font', 'font-size', 'font-family', 'font-weight',
|
||||
'border', 'border-top', 'border-bottom', 'border-left', 'border-right'],
|
||||
editor: $('<input>')
|
||||
};
|
||||
|
||||
@@ -4,4 +4,4 @@ $_SESSION['user'] = null;
|
||||
session_destroy();
|
||||
session_write_close();
|
||||
$user = null;
|
||||
header("Location:".CLIENT_BASE_URL."login.php");
|
||||
header("Location:".CLIENT_BASE_URL."login.php?login=no");
|
||||
@@ -61,6 +61,10 @@ class ICEHRM_Record extends ADOdb_Active_Record{
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function postProcessGetElement($obj){
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function getDefaultAccessLevel(){
|
||||
return array("get","element","save","delete");
|
||||
}
|
||||
|
||||
@@ -48,6 +48,17 @@ class Setting extends ICEHRM_Record {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function postProcessGetElement($obj){
|
||||
if($obj->name == 'Api: REST Api Token'){
|
||||
$user = BaseService::getInstance()->getCurrentUser();
|
||||
$dbUser = new User();
|
||||
$dbUser->Load("id = ?",array($user->id));
|
||||
$resp = RestApiManager::getInstance()->getAccessTokenForUser($dbUser);
|
||||
$obj->value = $resp->getData();
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
var $_table = 'Settings';
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,12 @@ if(SettingsManager::getInstance()->getSetting("System: Add New Permissions") ==
|
||||
SettingsManager::getInstance()->setSetting("System: Add New Permissions","0");
|
||||
}
|
||||
|
||||
$resetModuleNames = false;
|
||||
if(SettingsManager::getInstance()->getSetting("System: Reset Module Names") == "1"){
|
||||
$resetModuleNames = true;
|
||||
SettingsManager::getInstance()->setSetting("System: Reset Module Names","0");
|
||||
}
|
||||
|
||||
function includeModuleManager($type,$name,$data){
|
||||
$moduleCapsName = ucfirst($name);
|
||||
$moduleTypeCapsName = ucfirst($type); // Admin or Modules
|
||||
@@ -104,6 +110,12 @@ foreach($ams as $am){
|
||||
createPermissions($meta, $dbModule->id);
|
||||
}
|
||||
|
||||
if($resetModuleNames){
|
||||
$dbModule->label = $arr['label'];
|
||||
$dbModule->menu = $arr['menu'];
|
||||
$dbModule->icon = $arr['icon'];
|
||||
$dbModule->Save();
|
||||
}
|
||||
|
||||
$arr['name'] = $dbModule->name;
|
||||
$arr['label'] = $dbModule->label;
|
||||
@@ -191,6 +203,12 @@ foreach($ams as $am){
|
||||
createPermissions($meta, $dbModule->id);
|
||||
}
|
||||
|
||||
if($resetModuleNames){
|
||||
$dbModule->label = $arr['label'];
|
||||
$dbModule->menu = $arr['menu'];
|
||||
$dbModule->icon = $arr['icon'];
|
||||
$dbModule->Save();
|
||||
}
|
||||
|
||||
$arr['name'] = $dbModule->name;
|
||||
$arr['label'] = $dbModule->label;
|
||||
|
||||
39
src/rest.php
39
src/rest.php
@@ -6,27 +6,26 @@ include ("config.base.php");
|
||||
include ("include.common.php");
|
||||
include("server.includes.inc.php");
|
||||
|
||||
//clean request uri
|
||||
LogManager::getInstance()->info("REQUEST_URI :".$_SERVER['REQUEST_URI']);
|
||||
$parts = explode("?", $_SERVER['REQUEST_URI']);
|
||||
$uri = $parts[0];
|
||||
if(substr($uri, -1) == "/"){
|
||||
$uri = substr($uri, 0, -1);
|
||||
}
|
||||
if(SettingsManager::getInstance()->getSetting('Api: REST Api Enabled') == '1') {
|
||||
|
||||
LogManager::getInstance()->info("REQUEST_URI Cleaned :".$uri);
|
||||
|
||||
$type = strtolower($_SERVER['REQUEST_METHOD']);
|
||||
define('REST_API_PATH', '/api/');
|
||||
|
||||
LogManager::getInstance()->info("Request: " . $_REQUEST);
|
||||
|
||||
\NoahBuscher\Macaw\Macaw::get(REST_API_PATH . 'echo', function () {
|
||||
echo "Echo " . rand();
|
||||
});
|
||||
|
||||
$moduleManagers = BaseService::getInstance()->getModuleManagers();
|
||||
|
||||
foreach ($moduleManagers as $moduleManagerObj) {
|
||||
|
||||
$moduleManagerObj->setupRestEndPoints();
|
||||
}
|
||||
|
||||
\NoahBuscher\Macaw\Macaw::dispatch();
|
||||
|
||||
$supportedMethods = array('get','post','put','delete');
|
||||
if(!in_array($type, $supportedMethods)){
|
||||
echo json_encode(new IceResponse(IceResponse::ERROR, "Method not supported"));
|
||||
exit();
|
||||
}
|
||||
$response = RestApiManager::getInstance()->process($type, $uri, $_REQUEST);
|
||||
if($response->getStatus() == IceResponse::SUCCESS){
|
||||
echo json_encode($response,JSON_PRETTY_PRINT);
|
||||
}else{
|
||||
echo json_encode($response,JSON_PRETTY_PRINT);
|
||||
}
|
||||
exit();
|
||||
echo "REST Api is not enabled. Please set 'Api: REST Api Enabled' setting to true";
|
||||
}
|
||||
@@ -39,6 +39,7 @@ include (APP_BASE_PATH."classes/UIManager.php");
|
||||
include (APP_BASE_PATH."classes/RestApiManager.php");
|
||||
include (APP_BASE_PATH."classes/ModuleBuilder.php");
|
||||
include (APP_BASE_PATH."classes/SimpleImage.php");
|
||||
include (APP_BASE_PATH."classes/Macaw.php");
|
||||
include (APP_BASE_PATH."classes/crypt/Aes.php");
|
||||
include (APP_BASE_PATH."classes/crypt/AesCtr.php");
|
||||
|
||||
@@ -112,7 +113,8 @@ if(defined('CLIENT_PATH')){
|
||||
$moduleManagerObj->setupUserClasses($userTables);
|
||||
$moduleManagerObj->setupFileFieldMappings($fileFields);
|
||||
$moduleManagerObj->setupErrorMappings($mysqlErrors);
|
||||
$moduleManagerObj->setupRestEndPoints();
|
||||
//$moduleManagerObj->setupRestEndPoints();
|
||||
$moduleManagerObj->initCalculationHooks();
|
||||
|
||||
$modelClassList = $moduleManagerObj->getModelClasses();
|
||||
|
||||
|
||||
@@ -263,4 +263,34 @@ if($action == 'get'){
|
||||
|
||||
}
|
||||
|
||||
echo json_encode($ret);
|
||||
$res = json_encode($ret);
|
||||
|
||||
if(empty($res) && !empty($ret)){
|
||||
//Do this only if there is a json encoding error
|
||||
if(!empty($ret['object'])) {
|
||||
if (is_array($ret['object'])) {
|
||||
$newObjects = array();
|
||||
foreach ($ret['object'] as $obj) {
|
||||
$newObjects[] = BaseService::getInstance()->cleanNonUTFChar($obj);
|
||||
}
|
||||
$ret['object'] = $newObjects;
|
||||
} else {
|
||||
$ret['object'] = BaseService::getInstance()->cleanNonUTFChar($ret['object']);
|
||||
}
|
||||
}else if(!empty($ret['data'])){
|
||||
if (is_array($ret['data'])) {
|
||||
$newObjects = array();
|
||||
foreach ($ret['data'] as $obj) {
|
||||
$newObjects[] = BaseService::getInstance()->cleanNonUTFChar($obj);
|
||||
}
|
||||
$ret['data'] = $newObjects;
|
||||
} else {
|
||||
$ret['data'] = BaseService::getInstance()->cleanNonUTFChar($ret['data']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
echo json_encode($ret);
|
||||
}else{
|
||||
echo $res;
|
||||
}
|
||||
|
||||
608
src/utils/EvalMath.php
Normal file
608
src/utils/EvalMath.php
Normal file
@@ -0,0 +1,608 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
EvalMath - PHP Class to safely evaluate math expressions
|
||||
Copyright (C) 2005 Miles Kaufmann <http://www.twmagic.com/>
|
||||
|
||||
================================================================================
|
||||
|
||||
NAME
|
||||
EvalMath - safely evaluate math expressions
|
||||
|
||||
SYNOPSIS
|
||||
<?
|
||||
include('evalmath.class.php');
|
||||
$m = new EvalMath;
|
||||
// basic evaluation:
|
||||
$result = $m->evaluate('2+2');
|
||||
// supports: order of operation; parentheses; negation; built-in functions
|
||||
$result = $m->evaluate('-8(5/2)^2*(1-sqrt(4))-8');
|
||||
// create your own variables
|
||||
$m->evaluate('a = e^(ln(pi))');
|
||||
// or functions
|
||||
$m->evaluate('f(x,y) = x^2 + y^2 - 2x*y + 1');
|
||||
// and then use them
|
||||
$result = $m->evaluate('3*f(42,a)');
|
||||
?>
|
||||
|
||||
DESCRIPTION
|
||||
Use the EvalMath class when you want to evaluate mathematical expressions
|
||||
from untrusted sources. You can define your own variables and functions,
|
||||
which are stored in the object. Try it, it's fun!
|
||||
|
||||
METHODS
|
||||
$m->evalute($expr)
|
||||
Evaluates the expression and returns the result. If an error occurs,
|
||||
prints a warning and returns false. If $expr is a function assignment,
|
||||
returns true on success.
|
||||
|
||||
$m->e($expr)
|
||||
A synonym for $m->evaluate().
|
||||
|
||||
$m->vars()
|
||||
Returns an associative array of all user-defined variables and values.
|
||||
|
||||
$m->funcs()
|
||||
Returns an array of all user-defined functions.
|
||||
|
||||
PARAMETERS
|
||||
$m->suppress_errors
|
||||
Set to true to turn off warnings when evaluating expressions
|
||||
|
||||
$m->last_error
|
||||
If the last evaluation failed, contains a string describing the error.
|
||||
(Useful when suppress_errors is on).
|
||||
|
||||
AUTHOR INFORMATION
|
||||
Copyright 2005, Miles Kaufmann.
|
||||
|
||||
LICENSE
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1 Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class was heavily modified in order to get usefull spreadsheet emulation ;-)
|
||||
* skodak
|
||||
*
|
||||
*/
|
||||
if(!class_exists('EvalMath')) {
|
||||
class EvalMath
|
||||
{
|
||||
|
||||
/** @var string Pattern used for a valid function or variable name. Note, var and func names are case insensitive. */
|
||||
private static $namepat = '[a-z][a-z0-9_]*';
|
||||
|
||||
var $suppress_errors = false;
|
||||
var $last_error = null;
|
||||
|
||||
var $v = array(); // variables (and constants)
|
||||
var $f = array(); // user-defined functions
|
||||
var $vb = array(); // constants
|
||||
var $fb = array( // built-in functions
|
||||
'sin', 'sinh', 'arcsin', 'asin', 'arcsinh', 'asinh',
|
||||
'cos', 'cosh', 'arccos', 'acos', 'arccosh', 'acosh',
|
||||
'tan', 'tanh', 'arctan', 'atan', 'arctanh', 'atanh',
|
||||
'sqrt', 'abs', 'ln', 'log', 'exp', 'floor', 'ceil');
|
||||
|
||||
var $fc = array( // calc functions emulation
|
||||
'average' => array(-1), 'max' => array(-1), 'min' => array(-1),
|
||||
'mod' => array(2), 'pi' => array(0), 'power' => array(2),
|
||||
'round' => array(1, 2), 'sum' => array(-1), 'rand_int' => array(2),
|
||||
'rand_float' => array(0));
|
||||
|
||||
var $allowimplicitmultiplication;
|
||||
|
||||
public function __construct($allowconstants = false, $allowimplicitmultiplication = false)
|
||||
{
|
||||
if ($allowconstants) {
|
||||
$this->v['pi'] = pi();
|
||||
$this->v['e'] = exp(1);
|
||||
}
|
||||
$this->allowimplicitmultiplication = $allowimplicitmultiplication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Old syntax of class constructor. Deprecated in PHP7.
|
||||
*
|
||||
* @deprecated since Moodle 3.1
|
||||
*/
|
||||
public function EvalMath($allowconstants = false, $allowimplicitmultiplication = false)
|
||||
{
|
||||
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
|
||||
self::__construct($allowconstants, $allowimplicitmultiplication);
|
||||
}
|
||||
|
||||
function e($expr)
|
||||
{
|
||||
return $this->evaluate($expr);
|
||||
}
|
||||
|
||||
function evaluate($expr)
|
||||
{
|
||||
$this->last_error = null;
|
||||
$expr = trim($expr);
|
||||
if (substr($expr, -1, 1) == ';') $expr = substr($expr, 0, strlen($expr) - 1); // strip semicolons at the end
|
||||
//===============
|
||||
// is it a variable assignment?
|
||||
if (preg_match('/^\s*(' . self::$namepat . ')\s*=\s*(.+)$/', $expr, $matches)) {
|
||||
if (in_array($matches[1], $this->vb)) { // make sure we're not assigning to a constant
|
||||
return $this->trigger(get_string('cannotassigntoconstant', 'mathslib', $matches[1]));
|
||||
}
|
||||
if (($tmp = $this->pfx($this->nfx($matches[2]))) === false) return false; // get the result and make sure it's good
|
||||
$this->v[$matches[1]] = $tmp; // if so, stick it in the variable array
|
||||
return $this->v[$matches[1]]; // and return the resulting value
|
||||
//===============
|
||||
// is it a function assignment?
|
||||
} elseif (preg_match('/^\s*(' . self::$namepat . ')\s*\(\s*(' . self::$namepat . '(?:\s*,\s*' . self::$namepat . ')*)\s*\)\s*=\s*(.+)$/', $expr, $matches)) {
|
||||
$fnn = $matches[1]; // get the function name
|
||||
if (in_array($matches[1], $this->fb)) { // make sure it isn't built in
|
||||
return $this->trigger(get_string('cannotredefinebuiltinfunction', 'mathslib', $matches[1]));
|
||||
}
|
||||
$args = explode(",", preg_replace("/\s+/", "", $matches[2])); // get the arguments
|
||||
if (($stack = $this->nfx($matches[3])) === false) return false; // see if it can be converted to postfix
|
||||
for ($i = 0; $i < count($stack); $i++) { // freeze the state of the non-argument variables
|
||||
$token = $stack[$i];
|
||||
if (preg_match('/^' . self::$namepat . '$/', $token) and !in_array($token, $args)) {
|
||||
if (array_key_exists($token, $this->v)) {
|
||||
$stack[$i] = $this->v[$token];
|
||||
} else {
|
||||
return $this->trigger(get_string('undefinedvariableinfunctiondefinition', 'mathslib', $token));
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->f[$fnn] = array('args' => $args, 'func' => $stack);
|
||||
return true;
|
||||
//===============
|
||||
} else {
|
||||
return $this->pfx($this->nfx($expr)); // straight up evaluation, woo
|
||||
}
|
||||
}
|
||||
|
||||
function vars()
|
||||
{
|
||||
return $this->v;
|
||||
}
|
||||
|
||||
function funcs()
|
||||
{
|
||||
$output = array();
|
||||
foreach ($this->f as $fnn => $dat)
|
||||
$output[] = $fnn . '(' . implode(',', $dat['args']) . ')';
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return boolean Is this a valid var or function name?
|
||||
*/
|
||||
public static function is_valid_var_or_func_name($name)
|
||||
{
|
||||
return preg_match('/' . self::$namepat . '$/iA', $name);
|
||||
}
|
||||
|
||||
//===================== HERE BE INTERNAL METHODS ====================\\
|
||||
|
||||
// Convert infix to postfix notation
|
||||
function nfx($expr)
|
||||
{
|
||||
|
||||
$index = 0;
|
||||
$stack = new EvalMathStack;
|
||||
$output = array(); // postfix form of expression, to be passed to pfx()
|
||||
$expr = trim(strtolower($expr));
|
||||
|
||||
$ops = array('+', '-', '*', '/', '^', '_');
|
||||
$ops_r = array('+' => 0, '-' => 0, '*' => 0, '/' => 0, '^' => 1); // right-associative operator?
|
||||
$ops_p = array('+' => 0, '-' => 0, '*' => 1, '/' => 1, '_' => 1, '^' => 2); // operator precedence
|
||||
|
||||
$expecting_op = false; // we use this in syntax-checking the expression
|
||||
// and determining when a - is a negation
|
||||
|
||||
if (preg_match("/[^\w\s+*^\/()\.,-]/", $expr, $matches)) { // make sure the characters are all good
|
||||
return $this->trigger(get_string('illegalcharactergeneral', 'mathslib', $matches[0]));
|
||||
}
|
||||
|
||||
while (1) { // 1 Infinite Loop ;)
|
||||
$op = substr($expr, $index, 1); // get the first character at the current index
|
||||
// find out if we're currently at the beginning of a number/variable/function/parenthesis/operand
|
||||
$ex = preg_match('/^(' . self::$namepat . '\(?|\d+(?:\.\d*)?(?:(e[+-]?)\d*)?|\.\d+|\()/', substr($expr, $index), $match);
|
||||
//===============
|
||||
if ($op == '-' and !$expecting_op) { // is it a negation instead of a minus?
|
||||
$stack->push('_'); // put a negation on the stack
|
||||
$index++;
|
||||
} elseif ($op == '_') { // we have to explicitly deny this, because it's legal on the stack
|
||||
return $this->trigger(get_string('illegalcharacterunderscore', 'mathslib')); // but not in the input expression
|
||||
//===============
|
||||
} elseif ((in_array($op, $ops) or $ex) and $expecting_op) { // are we putting an operator on the stack?
|
||||
if ($ex) { // are we expecting an operator but have a number/variable/function/opening parethesis?
|
||||
if (!$this->allowimplicitmultiplication) {
|
||||
return $this->trigger(get_string('implicitmultiplicationnotallowed', 'mathslib'));
|
||||
} else {// it's an implicit multiplication
|
||||
$op = '*';
|
||||
$index--;
|
||||
}
|
||||
}
|
||||
// heart of the algorithm:
|
||||
while ($stack->count > 0 and ($o2 = $stack->last()) and in_array($o2, $ops) and ($ops_r[$op] ? $ops_p[$op] < $ops_p[$o2] : $ops_p[$op] <= $ops_p[$o2])) {
|
||||
$output[] = $stack->pop(); // pop stuff off the stack into the output
|
||||
}
|
||||
// many thanks: http://en.wikipedia.org/wiki/Reverse_Polish_notation#The_algorithm_in_detail
|
||||
$stack->push($op); // finally put OUR operator onto the stack
|
||||
$index++;
|
||||
$expecting_op = false;
|
||||
//===============
|
||||
} elseif ($op == ')' and $expecting_op) { // ready to close a parenthesis?
|
||||
while (($o2 = $stack->pop()) != '(') { // pop off the stack back to the last (
|
||||
if (is_null($o2)) return $this->trigger(get_string('unexpectedclosingbracket', 'mathslib'));
|
||||
else $output[] = $o2;
|
||||
}
|
||||
if (preg_match('/^(' . self::$namepat . ')\($/', $stack->last(2), $matches)) { // did we just close a function?
|
||||
$fnn = $matches[1]; // get the function name
|
||||
$arg_count = $stack->pop(); // see how many arguments there were (cleverly stored on the stack, thank you)
|
||||
$fn = $stack->pop();
|
||||
$output[] = array('fn' => $fn, 'fnn' => $fnn, 'argcount' => $arg_count); // send function to output
|
||||
if (in_array($fnn, $this->fb)) { // check the argument count
|
||||
if ($arg_count > 1) {
|
||||
$a = new stdClass();
|
||||
$a->expected = 1;
|
||||
$a->given = $arg_count;
|
||||
return $this->trigger(get_string('wrongnumberofarguments', 'mathslib', $a));
|
||||
}
|
||||
} elseif (array_key_exists($fnn, $this->fc)) {
|
||||
$counts = $this->fc[$fnn];
|
||||
if (in_array(-1, $counts) and $arg_count > 0) {
|
||||
} elseif (!in_array($arg_count, $counts)) {
|
||||
$a = new stdClass();
|
||||
$a->expected = implode('/', $this->fc[$fnn]);
|
||||
$a->given = $arg_count;
|
||||
return $this->trigger(get_string('wrongnumberofarguments', 'mathslib', $a));
|
||||
}
|
||||
} elseif (array_key_exists($fnn, $this->f)) {
|
||||
if ($arg_count != count($this->f[$fnn]['args'])) {
|
||||
$a = new stdClass();
|
||||
$a->expected = count($this->f[$fnn]['args']);
|
||||
$a->given = $arg_count;
|
||||
return $this->trigger(get_string('wrongnumberofarguments', 'mathslib', $a));
|
||||
}
|
||||
} else { // did we somehow push a non-function on the stack? this should never happen
|
||||
return $this->trigger(get_string('internalerror', 'mathslib'));
|
||||
}
|
||||
}
|
||||
$index++;
|
||||
//===============
|
||||
} elseif ($op == ',' and $expecting_op) { // did we just finish a function argument?
|
||||
while (($o2 = $stack->pop()) != '(') {
|
||||
if (is_null($o2)) return $this->trigger(get_string('unexpectedcomma', 'mathslib')); // oops, never had a (
|
||||
else $output[] = $o2; // pop the argument expression stuff and push onto the output
|
||||
}
|
||||
// make sure there was a function
|
||||
if (!preg_match('/^(' . self::$namepat . ')\($/', $stack->last(2), $matches))
|
||||
return $this->trigger(get_string('unexpectedcomma', 'mathslib'));
|
||||
$stack->push($stack->pop() + 1); // increment the argument count
|
||||
$stack->push('('); // put the ( back on, we'll need to pop back to it again
|
||||
$index++;
|
||||
$expecting_op = false;
|
||||
//===============
|
||||
} elseif ($op == '(' and !$expecting_op) {
|
||||
$stack->push('('); // that was easy
|
||||
$index++;
|
||||
$allow_neg = true;
|
||||
//===============
|
||||
} elseif ($ex and !$expecting_op) { // do we now have a function/variable/number?
|
||||
$expecting_op = true;
|
||||
$val = $match[1];
|
||||
if (preg_match('/^(' . self::$namepat . ')\($/', $val, $matches)) { // may be func, or variable w/ implicit multiplication against parentheses...
|
||||
if (in_array($matches[1], $this->fb) or array_key_exists($matches[1], $this->f) or array_key_exists($matches[1], $this->fc)) { // it's a func
|
||||
$stack->push($val);
|
||||
$stack->push(1);
|
||||
$stack->push('(');
|
||||
$expecting_op = false;
|
||||
} else { // it's a var w/ implicit multiplication
|
||||
$val = $matches[1];
|
||||
$output[] = $val;
|
||||
}
|
||||
} else { // it's a plain old var or num
|
||||
$output[] = $val;
|
||||
}
|
||||
$index += strlen($val);
|
||||
//===============
|
||||
} elseif ($op == ')') {
|
||||
//it could be only custom function with no params or general error
|
||||
if ($stack->last() != '(' or $stack->last(2) != 1) return $this->trigger(get_string('unexpectedclosingbracket', 'mathslib'));
|
||||
if (preg_match('/^(' . self::$namepat . ')\($/', $stack->last(3), $matches)) { // did we just close a function?
|
||||
$stack->pop();// (
|
||||
$stack->pop();// 1
|
||||
$fn = $stack->pop();
|
||||
$fnn = $matches[1]; // get the function name
|
||||
$counts = $this->fc[$fnn];
|
||||
if (!in_array(0, $counts)) {
|
||||
$a = new stdClass();
|
||||
$a->expected = $this->fc[$fnn];
|
||||
$a->given = 0;
|
||||
return $this->trigger(get_string('wrongnumberofarguments', 'mathslib', $a));
|
||||
}
|
||||
$output[] = array('fn' => $fn, 'fnn' => $fnn, 'argcount' => 0); // send function to output
|
||||
$index++;
|
||||
$expecting_op = true;
|
||||
} else {
|
||||
return $this->trigger(get_string('unexpectedclosingbracket', 'mathslib'));
|
||||
}
|
||||
//===============
|
||||
} elseif (in_array($op, $ops) and !$expecting_op) { // miscellaneous error checking
|
||||
return $this->trigger(get_string('unexpectedoperator', 'mathslib', $op));
|
||||
} else { // I don't even want to know what you did to get here
|
||||
return $this->trigger(get_string('anunexpectederroroccured', 'mathslib'));
|
||||
}
|
||||
if ($index == strlen($expr)) {
|
||||
if (in_array($op, $ops)) { // did we end with an operator? bad.
|
||||
return $this->trigger(get_string('operatorlacksoperand', 'mathslib', $op));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (substr($expr, $index, 1) == ' ') { // step the index past whitespace (pretty much turns whitespace
|
||||
$index++; // into implicit multiplication if no operator is there)
|
||||
}
|
||||
|
||||
}
|
||||
while (!is_null($op = $stack->pop())) { // pop everything off the stack and push onto output
|
||||
if ($op == '(') return $this->trigger(get_string('expectingaclosingbracket', 'mathslib')); // if there are (s on the stack, ()s were unbalanced
|
||||
$output[] = $op;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
// evaluate postfix notation
|
||||
function pfx($tokens, $vars = array())
|
||||
{
|
||||
|
||||
if ($tokens == false) return false;
|
||||
|
||||
$stack = new EvalMathStack;
|
||||
|
||||
foreach ($tokens as $token) { // nice and easy
|
||||
|
||||
// if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
|
||||
if (is_array($token)) { // it's a function!
|
||||
$fnn = $token['fnn'];
|
||||
$count = $token['argcount'];
|
||||
if (in_array($fnn, $this->fb)) { // built-in function:
|
||||
if (is_null($op1 = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib'));
|
||||
$fnn = preg_replace("/^arc/", "a", $fnn); // for the 'arc' trig synonyms
|
||||
if ($fnn == 'ln') $fnn = 'log';
|
||||
eval('$stack->push(' . $fnn . '($op1));'); // perfectly safe eval()
|
||||
} elseif (array_key_exists($fnn, $this->fc)) { // calc emulation function
|
||||
// get args
|
||||
$args = array();
|
||||
for ($i = $count - 1; $i >= 0; $i--) {
|
||||
if (is_null($args[] = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib'));
|
||||
}
|
||||
$res = call_user_func_array(array('EvalMathFuncs', $fnn), array_reverse($args));
|
||||
if ($res === FALSE) {
|
||||
return $this->trigger(get_string('internalerror', 'mathslib'));
|
||||
}
|
||||
$stack->push($res);
|
||||
} elseif (array_key_exists($fnn, $this->f)) { // user function
|
||||
// get args
|
||||
$args = array();
|
||||
for ($i = count($this->f[$fnn]['args']) - 1; $i >= 0; $i--) {
|
||||
if (is_null($args[$this->f[$fnn]['args'][$i]] = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib'));
|
||||
}
|
||||
$stack->push($this->pfx($this->f[$fnn]['func'], $args)); // yay... recursion!!!!
|
||||
}
|
||||
// if the token is a binary operator, pop two values off the stack, do the operation, and push the result back on
|
||||
} elseif (in_array($token, array('+', '-', '*', '/', '^'), true)) {
|
||||
if (is_null($op2 = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib'));
|
||||
if (is_null($op1 = $stack->pop())) return $this->trigger(get_string('internalerror', 'mathslib'));
|
||||
switch ($token) {
|
||||
case '+':
|
||||
$stack->push($op1 + $op2);
|
||||
break;
|
||||
case '-':
|
||||
$stack->push($op1 - $op2);
|
||||
break;
|
||||
case '*':
|
||||
$stack->push($op1 * $op2);
|
||||
break;
|
||||
case '/':
|
||||
if ($op2 == 0) return $this->trigger(get_string('divisionbyzero', 'mathslib'));
|
||||
$stack->push($op1 / $op2);
|
||||
break;
|
||||
case '^':
|
||||
$stack->push(pow($op1, $op2));
|
||||
break;
|
||||
}
|
||||
// if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
|
||||
} elseif ($token == "_") {
|
||||
$stack->push(-1 * $stack->pop());
|
||||
// if the token is a number or variable, push it on the stack
|
||||
} else {
|
||||
if (is_numeric($token)) {
|
||||
$stack->push($token);
|
||||
} elseif (array_key_exists($token, $this->v)) {
|
||||
$stack->push($this->v[$token]);
|
||||
} elseif (array_key_exists($token, $vars)) {
|
||||
$stack->push($vars[$token]);
|
||||
} else {
|
||||
return $this->trigger(get_string('undefinedvariable', 'mathslib', $token));
|
||||
}
|
||||
}
|
||||
}
|
||||
// when we're out of tokens, the stack should have a single element, the final result
|
||||
if ($stack->count != 1) return $this->trigger(get_string('internalerror', 'mathslib'));
|
||||
return $stack->pop();
|
||||
}
|
||||
|
||||
// trigger an error, but nicely, if need be
|
||||
function trigger($msg)
|
||||
{
|
||||
$this->last_error = $msg;
|
||||
if (!$this->suppress_errors) trigger_error($msg, E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// for internal use
|
||||
class EvalMathStack
|
||||
{
|
||||
|
||||
var $stack = array();
|
||||
var $count = 0;
|
||||
|
||||
function push($val)
|
||||
{
|
||||
$this->stack[$this->count] = $val;
|
||||
$this->count++;
|
||||
}
|
||||
|
||||
function pop()
|
||||
{
|
||||
if ($this->count > 0) {
|
||||
$this->count--;
|
||||
return $this->stack[$this->count];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function last($n = 1)
|
||||
{
|
||||
if ($this->count - $n >= 0) {
|
||||
return $this->stack[$this->count - $n];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// spreadsheet functions emulation
|
||||
class EvalMathFuncs
|
||||
{
|
||||
|
||||
static function average()
|
||||
{
|
||||
$args = func_get_args();
|
||||
return (call_user_func_array(array('self', 'sum'), $args) / count($args));
|
||||
}
|
||||
|
||||
static function max()
|
||||
{
|
||||
$args = func_get_args();
|
||||
$res = array_pop($args);
|
||||
foreach ($args as $a) {
|
||||
if ($res < $a) {
|
||||
$res = $a;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
static function min()
|
||||
{
|
||||
$args = func_get_args();
|
||||
$res = array_pop($args);
|
||||
foreach ($args as $a) {
|
||||
if ($res > $a) {
|
||||
$res = $a;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
static function mod($op1, $op2)
|
||||
{
|
||||
return $op1 % $op2;
|
||||
}
|
||||
|
||||
static function pi()
|
||||
{
|
||||
return pi();
|
||||
}
|
||||
|
||||
static function power($op1, $op2)
|
||||
{
|
||||
return pow($op1, $op2);
|
||||
}
|
||||
|
||||
static function round($val, $precision = 0)
|
||||
{
|
||||
return round($val, $precision);
|
||||
}
|
||||
|
||||
static function sum()
|
||||
{
|
||||
$args = func_get_args();
|
||||
$res = 0;
|
||||
foreach ($args as $a) {
|
||||
$res += $a;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected static $randomseed = null;
|
||||
|
||||
static function set_random_seed($randomseed)
|
||||
{
|
||||
self::$randomseed = $randomseed;
|
||||
}
|
||||
|
||||
static function get_random_seed()
|
||||
{
|
||||
if (is_null(self::$randomseed)) {
|
||||
return microtime();
|
||||
} else {
|
||||
return self::$randomseed;
|
||||
}
|
||||
}
|
||||
|
||||
static function rand_int($min, $max)
|
||||
{
|
||||
if ($min >= $max) {
|
||||
return false; //error
|
||||
}
|
||||
$noofchars = ceil(log($max + 1 - $min, '16'));
|
||||
$md5string = md5(self::get_random_seed());
|
||||
$stringoffset = 0;
|
||||
do {
|
||||
while (($stringoffset + $noofchars) > strlen($md5string)) {
|
||||
$md5string .= md5($md5string);
|
||||
}
|
||||
$randomno = hexdec(substr($md5string, $stringoffset, $noofchars));
|
||||
$stringoffset += $noofchars;
|
||||
} while (($min + $randomno) > $max);
|
||||
return $min + $randomno;
|
||||
}
|
||||
|
||||
static function rand_float()
|
||||
{
|
||||
$randomvalues = unpack('v', md5(self::get_random_seed(), true));
|
||||
return array_shift($randomvalues) / 65536;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user