Skip to content

Instantly share code, notes, and snippets.

@nexpr
Last active February 7, 2016 09:29
Show Gist options
  • Save nexpr/118fa2c47fae0b93c45c to your computer and use it in GitHub Desktop.
Save nexpr/118fa2c47fae0b93c45c to your computer and use it in GitHub Desktop.
phjs >=7.0.0
<?php
require "lib.php";
class EvE {
static $eve;
private $evs = [];
function on(...$args){
overload($args, [
[
"type" => ["string", "function", "int"],
"action" => function($evname, $evaction, $count){
$this->addEventListenerLimited($evname, $evaction, $count);
}
],[
"type" => ["string", "function"],
"action" => function($evname, $evaction){
$this->addEventListenerPermanent($evname, $evaction);
}
],[
"type" => null,
"action" => null
]
]);
}
private function addEventListenerPermanent($evname, $evaction){
$this->evs[$evname][] = [
"action" => $evaction,
"count" => -1
];
}
private function addEventListenerLimited($evname, $evaction, $count){
if($count <= 0){
throw new Exception("count must be positive integer value.");
}
$this->evs[$evname][] = [
"action" => $evaction,
"count" => $count
];
}
function off(...$args){
overload($args, [
[
"type" => ["string", "function"],
"action" => function($evname, $evaction){
$this->removeEventListener($evname, $evaction);
}
],[
"type" => ["string"],
"action" => function($evname){
$this->removeEventListenerByName($evname);
}
],[
"type" => ["function"],
"action" => function($evaction){
$this->removeEventListenerByAction($evaction);
}
],[
"type" => null,
"action" => null
]
]);
}
function clear(){
$this->evs = [];
}
private function removeEventListenerByName($evname){
unset($this->evs[$evname]);
}
private function removeEventListenerByAction($evaction){
foreach($this->evs as &$evactions){
foreach($evactions as $idx => $val){
if($val === $evaction) unset($evactions[$idx]);
}
}
}
private function removeEventListener($evname, $evaction){
if(isset($this->evs[$evname])){
foreach($this->evs[$evname] as $idx => $val){
if($val === $evaction) unset($this->evs[$evname][$idx]);
}
}
}
function emit($evname, ...$args){
if(isset($this->evs[$evname]) && is_array($this->evs[$evname])){
foreach($this->evs[$evname] as $idx => &$ev){
$is_stop = $ev["action"](...$args) === false;
if(--$ev["count"] === 0){
unset($this->evs[$evname][$idx]);
}
if($is_stop){
break;
}
}
}
}
}
EvE::$eve = new EvE();
<?php
function overload($args, $condition){
$result = null;
$typeMatch = function($args, $types){
if($types == null){
return true;
}
if(!is_array($types)){
return false;
}
foreach($types as $idx => $type){
$val = $args[$idx] ?? null;
switch($type){
case "int":
case "integer":
if(!is_int($val)) return false;
break;
case "float":
case "double":
if(!is_double($val)) return false;
break;
case "string":
if(!is_string($val)) return false;
break;
case "array":
if(!is_array($val)) return false;
break;
case "object":
if(!is_object($val)) return false;
break;
case "closure":
if(!($val instanceof Closure)) return false;
break;
case "function":
if(!is_callable($val)) return false;
break;
case null:
break;
default:
if(!($val instanceof $type))
return false;
}
}
return true;
};
foreach($condition as $ovfunc){
if($typeMatch($args, $ovfunc["type"])){
if(!$ovfunc["action"]){
throw new Exception("TypeError");
exit;
}else{
$result = $ovfunc["action"](...$args);
}
break;
}
}
return $result;
}
<?php
$ls = new LocalStorage("test");
$ls->set("a", "abc");
$ls->set("b", "bcd");
$ls->set("c", "cde");
$ls->set("d", "def");
var_dump($ls->get("a"));
$ls->remove("a");
var_dump($ls->get("a"));
$als = new ALocalStorage("test");
$als->get("b", function($value){var_dump($value);});
<?php
function setTimeout($cb, $msec, ...$args){
async("setTimeout", $msec, function($result) use ($cb, $args) {
$cb(...$args);
});
}
class LocalStorage {
const STORAGE_PATH = "phjs.storage";
protected $name = null;
function __construct(string $name){
if($name === "") throw new Exception("storage name must be needed.");
$this->name = $name;
}
function get(string $key){
$db = $this->getDB();
return $db[$this->name][$key] ?? null;
}
function set(string $key, $val){
$db = $this->getDB();
$db[$this->name][$key] = $val;
$this->update($db);
}
function remove(string $key){
$db = $this->getDB();
unset($db[$this->name][$key]);
$this->update($db);
}
private function update($db){
$json = json_encode($db);
file_put_contents(self::STORAGE_PATH, $json);
}
private function getDB(){
$json = file_get_contents(self::STORAGE_PATH);
$data = json_decode($json, true) ?? [];
return $data;
}
static function init(){
if(is_readable(self::STORAGE_PATH)){
$data = json_decode(file_get_contents(self::STORAGE_PATH), true);
if(is_array($data)){
return;
}
}
// not ok
file_put_contents(self::STORAGE_PATH, "{}");
}
}
LocalStorage::init();
class ALocalStorage extends LocalStorage {
function get(string $key, $cb){
$opt = [
"name" => $this->name,
"key" => $key
];
async("localStorage_get", $opt, function($result) use ($cb) {
$cb($result);
});
}
function set(string $key, $val, $cb){
$opt = [
"name" => $this->name,
"key" => $key,
"val" => $val
];
async("localStorage_set", $opt, function($result) use ($cb) {
$cb($result);
});
}
function remove(string $key, $cb){
$opt = [
"name" => $this->name,
"key" => $key,
];
async("localStorage_remove", $opt, function($result) use ($cb) {
$cb($result);
});
}
}
<?php
if($argc !== 3){
throw new Exception("arguments count must be 3. This script is executed by other script only.");
}
function main($fn_name, $tmp){
try{
$data = json_decode(file_get_contents($tmp), true);
$result = is_callable($fn_name) ? $fn_name($data) : [
"error" => "unknown async function `${fn_name}'"
];
}catch(Exception $e){
$result = [
"error" => $e->getMessage()
];
}
$result_path = $tmp . ".return";
$fp = fopen($result_path, "w");
flock($fp, LOCK_EX);
fwrite($fp, json_encode($result));
flock($fp, LOCK_UN);
fclose($fp);
}
function setTimeout($msec){
usleep($msec * 1000);
}
function localStorage_get($opt){
$name = $opt["name"];
$key = $opt["key"];
return (new LocalStorage($name))->get($key);
}
function localStorage_set($opt){
$name = $opt["name"];
$key = $opt["key"];
$value = $opt["value"];
return (new LocalStorage($name))->set($key, $set);
}
function localStorage_remove($opt){
$name = $opt["name"];
$key = $opt["key"];
return (new LocalStorage($name))->remove($key);
}
class LocalStorage {
const STORAGE_PATH = "phjs.storage";
protected $name = null;
function __construct(string $name){
if($name === "") throw new Exception("storage name must be needed.");
$this->name = $name;
}
function get(string $key){
$db = $this->getDB();
return $db[$this->name][$key] ?? null;
}
function set(string $key, $val){
$db = $this->getDB();
$db[$this->name][$key] = $val;
$this->update($db);
}
function remove(string $key){
$db = $this->getDB();
unset($db[$this->name][$key]);
$this->update($db);
}
private function update($db){
$json = json_encode($db);
file_put_contents(self::STORAGE_PATH, $json);
}
private function getDB(){
$json = file_get_contents(self::STORAGE_PATH);
$data = json_decode($json, true);
return $data;
}
static function init(){
if(is_readable(self::STORAGE_PATH)){
$data = json_decode(file_get_contents(self::STORAGE_PATH), true);
if(is_array($data)){
return;
}
}
// not ok
file_put_contents(self::STORAGE_PATH, "{}");
}
}
LocalStorage::init();
main($argv[1], $argv[2]);
<?php
// extends
Object::$prototype->dump = function($full = false){
if($full === false || $full() == false){
var_dump($this());
}else{
var_dump($this);
}
};
Object::$prototype->isArray = function(){
return $this->constructor === "__A__";
};
Object::$prototype->isString = function(){
return $this->constructor === "__S__";
};
Object::$prototype->isBoolean = function(){
return $this->constructor === "__B__";
};
Object::$prototype->isNumber = function(){
return $this->constructor === "__N__";
};
Object::$prototype->isNull = function(){
return $this->constructor === "__Null__";
};
Object::$prototype->isUndefined = function(){
return $this->constructor === "Undefined";
};
Object::$prototype->compareTo = function($arg){
return $this === $arg;
};
Object::$prototype->isTrue = function(){
return true;
};
__A__::$prototype->join = function($sep){
$sep = $this->raw($sep);
return join($sep, $this());
};
__S__::$prototype->isTrue = function(){
return $this() !== "";
};
__S__::$prototype->split = function($sep){
$sep = $this->raw($sep);
return explode($sep, $this());
};
__B__::$prototype->isTrue = function(){
return $this();
};
__N__::$prototype->isTrue = function(){
return $this() !== 0;
};
__F__::$prototype->call = function($bindThis, ...$args){
return $this()->bindTo($bindThis())(...$args);
};
__F__::$prototype->apply = function($bindThis, $args){
$args = array_map("_", $args());
return $this()->bindTo($bindThis())(...$args);
};
__Null__::$prototype->isTrue = function(){
return false;
};
Undefined::$prototype->isTrue = function(){
return false;
};
<?php
// Object
class Object{
// search property with prim and following prototype chain, and call it
function __call($name, $arguments){
$meth = null;
if(is_object($this->prim) && property_exists($this->prim, $name)){
$meth = $this->prim->$name;
}
if(is_object($this->__proto__)){
$meth = $this->__proto__->$name;
}
if($meth instanceof Undefined){
throw new Exception("`{$name}' is not defined.");
}
if(!is_callable($meth)){
throw new Exception("`{$name}' is not function.");
}
$arguments = array_map("_", $arguments);
return _($meth->bindTo($this)(...$arguments));
}
// search property with prim and following prototype chain
function __get($name){
if(is_object($this->prim) && property_exists($this->prim, $name)){
return $this->prim->$name;
}
if(is_object($this->__proto__)){
return $this->__proto__->$name;
}else{
return new Undefined();
}
}
// set property of prim when prim is object
function __set($name, $value){
if(is_object($this->prim)){
$this->prim->$name = $value;
}
}
// get raw prim value
function __invoke(){
$prim = $this->prim;
while($prim instanceof Object){
$prim = $prim->prim;
}
return $prim;
}
function __construct($data = null){
if(isset(static::$prototype)){
$this->__proto__ = static::$prototype;
}
if(get_class($this) === get_class()){
$this->prim = new StdClass();
}else{
$this->prim = $data;
}
}
function raw(...$args){
if(count($args) === 0){
return $this();
}else{
$raws = array_map(function($arg){
return $arg instanceof Object ? $arg() : $arg;
}, $args);
return count($args) === 1 ? array_pop($raws) : $raws;
}
}
public $prim = null;
public $__proto__ = null;
static $prototype = null;
}
Object::$prototype = new Object();
Object::$prototype->constructor = "Object";
Object::$prototype->toString = function(){
return json_encode($this());
};
// Array
class __A__ extends Object{
function __get($key){
if($key === 'length'){
return count($this());
}
return parent::__get($key);
}
function __invoke($pos = null){
if(func_num_args() == 0){
return parent::__invoke();
}else{
$pos = $this->raw($pos);
$arr = $this();
return array_key_exists($pos, $arr) ? _($arr[$pos]) : new Undefined();
}
}
static $prototype = null;
}
__A__::$prototype = new Object();
__A__::$prototype->constructor = "__A__";
// String
class __S__ extends Object{
function __get($key){
if($key === 'length'){
return strlen($this());
}
return parent::__get($key);
}
function __invoke($pos = null){
if(func_num_args() == 0){
return parent::__invoke();
}else{
$pos = $this->raw($pos);
$str = $this();
return _(mb_substr($str, $pos, 1));
}
}
static $prototype = null;
}
__S__::$prototype = new Object();
__S__::$prototype->constructor = "__S__";
__S__::$prototype->toString = function(){
return $this();
};
// Boolean
class __B__ extends Object{
static $prototype = null;
}
__B__::$prototype = new Object();
__B__::$prototype->constructor = "__B__";
// Number
class __N__ extends Object{
static $prototype = null;
}
__N__::$prototype = new Object();
__N__::$prototype->constructor = "__N__";
// Function
class __F__ extends Object{
static $prototype = null;
}
__F__::$prototype = new Object();
__F__::$prototype->constructor = "__F__";
__F__::$prototype->toString = function(){
return "function (){}";
};
// null
class __Null__ extends Object{
static $prototype = null;
}
__Null__::$prototype = new Object();
__Null__::$prototype->constructor = "__Null__";
__Null__::$prototype->toString = function(){
return "null";
};
// undefined
class Undefined extends Object{
static $prototype = null;
}
Undefined::$prototype = new Object();
Undefined::$prototype->constructor = "Undefined";
Undefined::$prototype->toString = function(){
return "undefined";
};
class RegExp extends Object{
static $prototype = null;
}
RegExp::$prototype = new Object();
RegExp::$prototype->constructor = "RegExp";
class Date extends Object{
static $prototype = null;
}
Date::$prototype = new Object();
Date::$prototype->constructor = "Date";
function _($prim = null){
if(func_num_args() === 0){
return new Undefined($prim);
}
if($prim instanceof Object){
return $prim;
}
if($prim instanceof Closure){
return new __F__($prim);
}
if(is_array($prim)){
return new __A__($prim);
}
if(is_string($prim)){
return new __S__($prim);
}
if(is_bool($prim)){
return new __B__($prim);
}
if(is_numeric($prim)){
return new __N__($prim);
}
if(is_null($prim)){
return new __Null__($prim);
}
return new Object($prim);
}
<?php
require_once "phjs.php";
require_once "phjs.ex.php";
require_once "phjs.async.php";
require_once "EvE.php";
require_once "Promise.php";
function bgExec($fn_name, $data = null){
$dir = sys_get_temp_dir();
$io_path = tempnam($dir, "phjs");
file_put_contents($io_path, json_encode($data));
$command = "php7 phjs.bg.php ${fn_name} ${io_path}";
if(PHP_OS === "WINNT"){
pclose(popen("start /B ${command}", "w"));
}else if(PHP_OS === "Linux"){
shell_exec($command . " &");
}else{
throw new Exception("Error: [Unknown OS] cannot start backgroud tasks.");
}
return $io_path;
}
function asyncSet($io_path, $cb){
global $main_queue;
$main_queue[] = function() use ($io_path, $cb) {
$result_path = $io_path . ".return";
if(!is_readable($result_path)){
return false;
}
$fp = fopen($result_path, "r");
if(!flock($fp, LOCK_SH|LOCK_NB, $wouldblock)){
if($wouldblock){
return false;
}
}else{
flock($fp, LOCK_UN);
fclose($fp);
}
$result = json_decode(file_get_contents($result_path), true);
$cb($result);
unlink($io_path);
unlink($result_path);
return true;
};
}
function async($key, $bg_opt, $cb){
$io_path = bgExec($key, $bg_opt);
asyncSet($io_path, $cb);
}
function postpone($cb){
global $main_queue;
$main_queue[] = function(){
$cb();
return true;
};
}
if(empty($argv[1]) || !is_readable($argv[1])){
$filename = $argv[1] ?? "";
echo "Error! Invalid argument: Cannot open `${filename}'.", PHP_EOL;
echo "Usage: `php this-script program.php'", PHP_EOL;
exit;
}
$main_queue = [function() use ($argv) {
require $argv[1];
return true;
}];
// main
while(count($main_queue)){
foreach($main_queue as $idx => $fn){
if($fn()) unset($main_queue[$idx]);
}
}
<?php
require_once "phjs.php";
require_once "phjs.ex.php";
require_once "EvE.php";
function asyncExec($fn){
global $main_queue;
array_push($main_queue, $fn);
}
function setTimeout($fn, $time, ...$args){
$exec_time = microtime(true) + $time / 1000;
$recur = function() use($fn, $args, $exec_time, &$recur){
$exec_time < microtime(true) ?
$fn(...$args) :
asyncExec($recur);
};
asyncExec($recur);
}
$main_queue = [];
if(empty($argv[1]) || !is_readable($argv[1])){
$filename = $argv[1] ?? "";
echo "Error! Invalid argument: Cannot open `${filename}'.", PHP_EOL;
echo "Usage: `php this-script program.php'", PHP_EOL;
exit;
}
// set main script to queue
array_push($main_queue, function() use ($argv) {
require $argv[1];
});
// main loop
while(count($main_queue)){
$fn = array_shift($main_queue);
$fn();
}
<?php
$x = 1;
switch($x){
case 1:
$p = new Promise(function($a,$b){setTimeout($a, 1000, 10);});
$p->then(function($e){var_dump($e);});
$p->then(function($e){var_dump($e+1);});
$p->then(function($e){var_dump($e+2);});
// 10
// 11
// 12
break;
case 2:
$p = new Promise(function($a,$b){setTimeout($a, 1000, 16);});
$p->then(function($e){
return $e / 4;
})->then(function($e){
return $e + 2;
})->then(function($e){
var_dump($e * $e);
});
// 36
break;
case 3:
$p = new Promise(function($a,$b){setTimeout($b, 1000, 16);});
$p->then(function($e){var_dump($e);})->catch(function($e){var_dump(-$e);});
// -16
break;
case 4:
(new Promise(function($a,$b){setTimeout($a, 1000, 1);}))
->then(function($e){return new Promise(function($a,$b) use($e) {setTimeout($a, 1000, $e+1);});})
->then(function($e){var_dump($e);});
// 2
break;
case 5:
(new Promise(function($a,$b){$a(null);}))
->then(function($e){return Promise::reject();})
->catch(function($e){var_dump(111);});
// 111
break;
case 6:
(new Promise(function($a,$b){$a(1);}))
->then(function($e){throw new Exception("errrrrror");})
->catch(function($e){var_dump($e);});
// Error
break;
case 7:
(new Promise(function($a,$b){throw new Exception("errrror");}))
->then(function($e){return 1;})
->catch(function($e){var_dump($e);});
// Error
break;
}
<?php
class Promise {
const STATUS_PENDING = 0;
const STATUS_RESOLVED = 1;
const STATUS_REJECTED = 2;
private $status = self::STATUS_PENDING;
private $value = null;
private $nexts = [];
function __construct($fn){
$pcbmaker = function($status){
return function($value = null) use($status) {
if($this->status === self::STATUS_PENDING){
$this->status = $status;
$this->value = $value;
$this->callNexts();
}
};
};
$resolve = $pcbmaker(self::STATUS_RESOLVED);
$reject = $pcbmaker(self::STATUS_REJECTED);
try{
$fn($resolve, $reject);
}catch(Exception $err){
$reject($err);
}
}
private function callNexts(){
while(count($this->nexts)) array_shift($this->nexts)();
}
function then($fn){
return $this->setNext($fn, self::STATUS_RESOLVED);
}
function catch($fn){
return $this->setNext($fn, self::STATUS_REJECTED);
}
private function setNext($fn, $status){
return new self(function($resolve,$reject) use($fn, $status) {
$action = function() use($fn, $status, $resolve, $reject) {
if($this->status === $status){
try{
$result = $fn($this->value);
}catch(Exception $err){
$reject($err);
return;
}
if($result instanceof self){
$result->then(function($value) use($resolve) {$resolve($value);});
$result->catch(function($err) use($reject) {$reject($err);});
}else{
$resolve($result);
}
}else{
$this->status === self::STATUS_RESOLVED ? $resolve($this->value) : $reject($this->value);
}
};
if($this->status === self::STATUS_PENDING){
$this->nexts[] = $action;
}else{
$action();
}
});
}
static function resolve($value = null){
return new self(function ($a, $b) use($value) {$a($value);});
}
static function reject($value = null){
return new self(function ($a, $b) use($value) {$b($value);});
}
static function all($arr){
return new self(function ($a, $b) use($arr) {
$rem = count($arr);
$result = [];
foreach($arr as $idx => $promise){
if($promise instanceof self){
$promise->then(function($value) use(&$result, &$rem, $a) {
$rem--;
$result[$idx] = $value;
if($rem === 0){
$a($result);
}
});
$promise->catch(function($err) use($b) {
$b($err);
});
}else{
$result[$idx] = $promise;
}
}
});
}
static function race($arr){
return new self(function ($a, $b) use($arr) {
foreach($arr as $promise){
if($promise instanceof self){
$promise->then(function($value) use($a) {
$a($value);
});
$promise->catch(function($err) use($b) {
$b($err);
});
}else{
$a($promise);
}
}
});
}
}
EvE.php
イベントエミッター
独立してる
lib.php
phjs に依存しない系関数など
Promise.php
プロミス
独立してるけど 非同期部分がないと存在価値が微妙
phjs.php
JavaScript 風にメソッドチェーンとプロトタイプチェーンできる仕組み
phjs.ex.php
phjs.php のメソッド拡張
phjs.old.php
phjs.php の古い版(primの持ち方とか)
phjs.start.php
開始用のファイル
メインループ・非同期機能
phjs.start.mp.php
マルチプロセス版の開始ファイル
メインループ・非同期機能
phjs.async.php
マルチプロセス版の非同期関数置き場
phjs.bg.php
マルチプロセス版のバックグラウンド側の処理部分
sample.php
使い方サンプル
ls-sample.php
ローカルストレージのサンプル
promise-sample.php
プロミスのサンプル
usage.php
使い方(起動方法)
phjs.start.mp.php
require
→ phjs.php
→ phjs.ex.php
→ phjs.async.php
→ EvE.php
→ Promise.php
shell exec
→ phjs.bg.php
phjs.start.php
require
→ phjs.php
→ phjs.ex.php
→ EvE.php
sample.php
どっちでも
→ phjs.start.mp.php
→ phjs.start.php
<?php
// EventEmitter
// register listeners
EvE::$eve->on("in-settimeout", function($val){
echo "EvE: ", $val, PHP_EOL;
});
// setTimeout
// these 'echo' display at last.
setTimeout(function($a){
echo $a, PHP_EOL;
EvE::$eve->emit("in-settimeout", $a);
}, 1000, "SETTIMTOUT 1000 msec");
setTimeout(function($a){
echo $a, PHP_EOL;
EvE::$eve->emit("in-settimeout", $a);
}, 200, "SETTIMEOUT 200 msec");
echo "timeout setting done.";
// js like method
$a = _([1,2]);
var_dump($a->join("U")->dump());
$s = _("acd");
var_dump( $s() );
$a = _();
var_dump( $a->isTrue()() );
$f = _(function($a, $b){return $a() * $b();});
$f->call(null, 12, 12)->dump();
$f->apply(null, [20,30])->dump();
echo "message of EventEmitter and setTimeout are printed below.", PHP_EOL;
# single
php phjs.start.php sample.php
# multi
php phjs.start.mp.php sample.php
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment