Browse Source

init

master
一叶之秋 4 years ago
parent
commit
2d24dc7832
82 changed files with 13830 additions and 1 deletions
  1. +22
    -0
      DOCUMENTS/LICENSE
  2. +19
    -0
      DOCUMENTS/related_modules.md
  3. +25
    -1
      README.md
  4. +7
    -0
      config/cron/SampleBuilder.cron
  5. +71
    -0
      config/jenkins/Jenkinsfile.beta
  6. +71
    -0
      config/jenkins/Jenkinsfile.prod
  7. +82
    -0
      config/jenkins/Jenkinsfile.test
  8. +39
    -0
      config/kubernetes/gq-hello-deployment.yaml
  9. +39
    -0
      config/kubernetes/gq-hello-nginx-deployment.yaml
  10. +26
    -0
      config/kubernetes/gq-hello-nginx-ingress.yaml
  11. +24
    -0
      config/kubernetes/gq-hello-nginx-service.yaml
  12. +24
    -0
      config/kubernetes/gq-hello-service.yaml
  13. +71
    -0
      config/nginx/gq-hello.public.conf
  14. +21
    -0
      gq-hello-nginx.dockerfile
  15. +25
    -0
      gq-hello.dockerfile
  16. +1
    -0
      lock/placeholder.lock
  17. +0
    -0
      logs/.gitkeep
  18. +18
    -0
      src/SampleApi.php
  19. +50
    -0
      src/protected/config/main.config.php
  20. +122
    -0
      src/protected/controller/BaseController.php
  21. +110
    -0
      src/protected/controller/SampleController.php
  22. +48
    -0
      src/protected/h/SampleController.requirements.h.php
  23. +13
    -0
      src/protected/h/public_key.h.php
  24. +256
    -0
      src/protected/lib/algorithm/TrieTree.php
  25. +132
    -0
      src/protected/lib/base/AccessibilityCheck.php
  26. +67
    -0
      src/protected/lib/base/AutoRequire.php
  27. +72
    -0
      src/protected/lib/base/Config.php
  28. +150
    -0
      src/protected/lib/base/Feedback.php
  29. +155
    -0
      src/protected/lib/base/Input.php
  30. +198
    -0
      src/protected/lib/base/Log.php
  31. +130
    -0
      src/protected/lib/base/ObjectArray.php
  32. +3484
    -0
      src/protected/lib/cache/Predis_Client.php
  33. +59
    -0
      src/protected/lib/cache/RedisFactory.php
  34. +104
    -0
      src/protected/lib/cache/RedisProxy.php
  35. +159
    -0
      src/protected/lib/encrypt/Encrypt.php
  36. +7
    -0
      src/protected/lib/encrypt/Encrypt.test.php
  37. +1393
    -0
      src/protected/lib/html/simple_html_dom.php
  38. +97
    -0
      src/protected/lib/network/Curl.php
  39. +28
    -0
      src/protected/lib/network/Ip.php
  40. +230
    -0
      src/protected/lib/network/MultiCurl.php
  41. +3
    -0
      src/protected/lib/network/MultiCurlException.php
  42. +29
    -0
      src/protected/lib/network/MultiCurlManager.php
  43. +71
    -0
      src/protected/lib/network/MultiCurlSequence.php
  44. +59
    -0
      src/protected/lib/queue/AMQPFactory.php
  45. +106
    -0
      src/protected/lib/queue/AMQPProxy.php
  46. +291
    -0
      src/protected/lib/storage/BaseQuery.php
  47. +230
    -0
      src/protected/lib/storage/CommonQuery.php
  48. +72
    -0
      src/protected/lib/storage/DeleteQuery.php
  49. +303
    -0
      src/protected/lib/storage/File.php
  50. +23
    -0
      src/protected/lib/storage/FluentLiteral.php
  51. +133
    -0
      src/protected/lib/storage/FluentPDO.php
  52. +29
    -0
      src/protected/lib/storage/FluentStructure.php
  53. +26
    -0
      src/protected/lib/storage/FluentUtils.php
  54. +124
    -0
      src/protected/lib/storage/InsertQuery.php
  55. +57
    -0
      src/protected/lib/storage/MongoDBFactory.php
  56. +57
    -0
      src/protected/lib/storage/MysqlFactory.php
  57. +123
    -0
      src/protected/lib/storage/MysqlProxy.php
  58. +124
    -0
      src/protected/lib/storage/ReplaceQuery.php
  59. +133
    -0
      src/protected/lib/storage/SelectQuery.php
  60. +91
    -0
      src/protected/lib/storage/UpdateQuery.php
  61. +52
    -0
      src/protected/lib/string/String.php
  62. +32
    -0
      src/protected/lib/system/Process.php
  63. +60
    -0
      src/protected/lib/xml/Sxml.php
  64. +39
    -0
      src/protected/model/ApiProxy.php
  65. +29
    -0
      src/protected/model/FeedbackInfo.php
  66. +60
    -0
      src/protected/model/Request.php
  67. +18
    -0
      src/protected/model/Result.php
  68. +50
    -0
      src/protected/model/SampleApiProxy.php
  69. +63
    -0
      src/protected/model/SampleCache.php
  70. +89
    -0
      src/protected/model/SampleMongoDBStorage.php
  71. +46
    -0
      src/protected/model/SampleMySQLStorage.php
  72. +117
    -0
      src/protected/model/SampleRequest.php
  73. +64
    -0
      src/protected/model/SampleResult.php
  74. +92
    -0
      src/protected/views/BaseRender.php
  75. +120
    -0
      src/protected/views/BaseView.php
  76. +10
    -0
      src/status.php
  77. +29
    -0
      tests/ObjectArray.test.php
  78. +69
    -0
      tests/TestBase.php
  79. +21
    -0
      tests/lib/Config.phpt
  80. +17
    -0
      tests/lib/Encrypt.test.php
  81. +91
    -0
      tests/lib/Input.test.php
  82. +2929
    -0
      tests/run-tests.php

+ 22
- 0
DOCUMENTS/LICENSE View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 M.Karminski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 19
- 0
DOCUMENTS/related_modules.md View File

@ -0,0 +1,19 @@
related_modules.md
-------------------
```
@version 151109:1
@author karminski <code.karminski@outlook.com>
```
PHP Module
------------
- curl
- json
- PDO
- pdo_mysql
- phpredis
- https://github.com/phpredis/phpredis
- mongodb
- rdkafka
- yac

+ 25
- 1
README.md View File

@ -1,2 +1,26 @@
# gq-hello-container
Saltfish Raw PHP
----------------
```
@version 180426:1
@author karminski <code.karminski@outlook.com>
```
Name
----
saltfish_raw_php
Table of Contents
-----------------
* [Name](#name)
* [Desc](#desc)
sdgdfghdf
Desc
----
saltfish API generator base framework PHP version.

+ 7
- 0
config/cron/SampleBuilder.cron View File

@ -0,0 +1,7 @@
# SampleBuilder.cron
# @version: 160309:1
# @author: karminski <code.karminski@outlook.com>
#
# SampleBuilder cronjob
0 0 */1 * * root flock -xn /data/repo/saltfish_raw_php/lock/SampleBuilder.lock -c '/data/apps/php7/bin/php /data/repo/saltfish_raw_php/src/SampleBuilder.php --signal_file=/data/repo/saltfish_raw_php/lock/SampleBuilder.lock & >> /data/repo/saltfish_raw_php/logs/SampleBuilder.stdout.log'

+ 71
- 0
config/jenkins/Jenkinsfile.beta View File

@ -0,0 +1,71 @@
// Jenkinsfile.beta
// @version 180820:1
// @author zhangxuhong <zhangxuhong@xitu.io>
//
def appName = "suid-generator"
def label = "worker-${UUID.randomUUID().toString()}"
def registryHost = "harbor02.juejin.id/beta/"
def deployLocation = "beta.kube01.lobj.juejin.id"
podTemplate(
label: label,
containers: [
containerTemplate(
name: 'git',
image: 'harbor02.juejin.id/infrastructure/git-1.8.3.1-centos:0.0.3',
command: 'cat',
ttyEnabled: true
),
containerTemplate(
name: 'dind',
image: 'harbor02.juejin.id/infrastructure/docker-18.06.0-ce-dind:0.0.2',
command: 'cat',
ttyEnabled: true
),
containerTemplate(
name: 'kubectl',
image: 'harbor02.juejin.id/infrastructure/kubectl-1.11.1-centos-with-cert-cluster01.lobj:latest',
command: 'cat',
ttyEnabled: true
),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
],
cloud: deployLocation
){
node(label) {
// start deploy phrase
stage("[1/4] check out"){
container('git'){
checkout scm
sh "git rev-parse --short HEAD > commit-id"
tag = readFile('commit-id').replace("\n", "").replace("\r", "")
imageName = "${registryHost}${appName}:${tag}"
env.BUILDIMG=imageName
}
}
stage("[2/4] Build"){
container('dind'){
sh "docker version"
sh "docker build -t ${imageName} ./"
}
}
stage("[3/4] Push"){
container('dind'){
sh "/usr/local/bin/docker-login.sh"
sh "docker push ${imageName}"
}
}
stage("[4/4] Deploy"){
container('kubectl'){
sh "sed -i 's#__IMAGE__#$BUILDIMG#g' ./config/kubernetes/Deployment.yaml"
sh "cat ./config/kubernetes/Deployment.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/Service.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/Ingress.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/Deployment.yaml"
}
}
}
}

+ 71
- 0
config/jenkins/Jenkinsfile.prod View File

@ -0,0 +1,71 @@
// Jenkinsfile.prod
// @version 180820:1
// @author zhangxuhong <zhangxuhong@xitu.io>
//
def appName = "suid-generator"
def label = "worker-${UUID.randomUUID().toString()}"
def registryHost = "harbor02.juejin.id/prod/"
def deployLocation = "prod.kube01.lobj.juejin.id"
podTemplate(
label: label,
containers: [
containerTemplate(
name: 'git',
image: 'harbor02.juejin.id/infrastructure/git-1.8.3.1-centos:0.0.3',
command: 'cat',
ttyEnabled: true
),
containerTemplate(
name: 'dind',
image: 'harbor02.juejin.id/infrastructure/docker-18.06.0-ce-dind:0.0.2',
command: 'cat',
ttyEnabled: true
),
containerTemplate(
name: 'kubectl',
image: 'harbor02.juejin.id/infrastructure/kubectl-1.11.1-centos-with-cert-cluster01.lobj:latest',
command: 'cat',
ttyEnabled: true
),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
],
cloud: deployLocation
){
node(label) {
// start deploy phrase
stage("[1/4] check out"){
container('git'){
checkout scm
sh "git rev-parse --short HEAD > commit-id"
tag = readFile('commit-id').replace("\n", "").replace("\r", "")
imageName = "${registryHost}${appName}:${tag}"
env.BUILDIMG=imageName
}
}
stage("[2/4] Build"){
container('dind'){
sh "docker version"
sh "docker build -t ${imageName} ./"
}
}
stage("[3/4] Push"){
container('dind'){
sh "/usr/local/bin/docker-login.sh"
sh "docker push ${imageName}"
}
}
stage("[4/4] Deploy"){
container('kubectl'){
sh "sed -i 's#__IMAGE__#$BUILDIMG#g' ./config/kubernetes/Deployment.yaml"
sh "cat ./config/kubernetes/Deployment.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/Service.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/Ingress.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/Deployment.yaml"
}
}
}
}

+ 82
- 0
config/jenkins/Jenkinsfile.test View File

@ -0,0 +1,82 @@
// Jenkinsfile.test
// @version 180903:2
// @author zhangxuhong <zhangxuhong@xitu.io>
//
def repoName = "gq-hello"
def nginxRepoName = "${repoName}-nginx"
def label = "worker-${UUID.randomUUID().toString()}"
def registryHost = "harbor02.juejin.id/test/"
def deployLocation = "test.kube01.lobj.juejin.id"
podTemplate(
label: label,
containers: [
containerTemplate(
name: 'git',
image: 'harbor02.juejin.id/infrastructure/git-1.8.3.1-centos:0.0.3',
command: 'cat',
ttyEnabled: true
),
containerTemplate(
name: 'dind',
image: 'harbor02.juejin.id/infrastructure/docker-18.06.0-ce-dind:0.0.2',
command: 'cat',
ttyEnabled: true
),
containerTemplate(
name: 'kubectl',
image: 'harbor02.juejin.id/infrastructure/kubectl-1.11.1-centos-with-cert-cluster01.lobj:latest',
command: 'cat',
ttyEnabled: true
),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
],
cloud: deployLocation
){
node(label) {
// start deploy phrase
stage("[1/4] check out"){
container('git'){
checkout scm
sh "git rev-parse --short HEAD > commit-id"
tag = readFile('commit-id').replace("\n", "").replace("\r", "")
imageName = "${registryHost}${repoName}:${tag}"
nginxImageName = "${registryHost}${nginxRepoName}:${tag}"
env.IMAGE_NAME = imageName
env.NGINX_IMAGE_NAME = nginxImageName
}
}
stage("[2/4] Build"){
container('dind'){
sh "docker version"
sh "docker build -f gq-hello.dockerfile -t ${imageName} ./"
sh "docker build -f gq-hello-nginx.dockerfile -t ${nginxImageName} ./"
}
}
stage("[3/4] Push"){
container('dind'){
sh "/usr/local/bin/docker-login.sh"
sh "docker push ${imageName}"
sh "docker push ${nginxImageName}"
}
}
stage("[4/4] Deploy"){
container('kubectl'){
// deploy nginx
sh "sed -i 's#__IMAGE__#$NGINX_IMAGE_NAME#g' ./config/kubernetes/gq-hello-nginx-deployment.yaml"
sh "cat ./config/kubernetes/gq-hello-nginx-deployment.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/gq-hello-nginx-service.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/gq-hello-nginx-ingress.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/gq-hello-nginx-deployment.yaml"
// deploy repo
sh "sed -i 's#__IMAGE__#$IMAGE_NAME#g' ./config/kubernetes/gq-hello-deployment.yaml"
sh "cat ./config/kubernetes/gq-hello-deployment.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/gq-hello-service.yaml"
sh "kubectl --kubeconfig=/root/.kube/config apply -f ./config/kubernetes/gq-hello-deployment.yaml"
}
}
}
}

+ 39
- 0
config/kubernetes/gq-hello-deployment.yaml View File

@ -0,0 +1,39 @@
# gq-hello-deployment.yaml
#
# @version 180806:2
# @author zhangxuhong <zhangxuhong@xitu.io>
kind: Deployment
apiVersion: apps/v1
metadata:
name: gq-hello
labels:
name: gq-hello
role: backend
pl: php
application: php
version: 7.2.9
division: infrastructure
spec:
replicas: 3
selector:
matchLabels:
name: gq-hello
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
template:
metadata:
labels:
name: gq-hello
spec:
containers:
- name: gq-hello
image: __IMAGE__
imagePullPolicy: Always
ports:
- name: gq-hello
containerPort: 9000
protocol: TCP

+ 39
- 0
config/kubernetes/gq-hello-nginx-deployment.yaml View File

@ -0,0 +1,39 @@
# gq-hello-nginx-deployment.yaml
#
# @version 180806:2
# @author zhangxuhong <zhangxuhong@xitu.io>
kind: Deployment
apiVersion: apps/v1
metadata:
name: gq-hello-nginx
labels:
name: gq-hello-nginx
role: backend
pl: c
application: nginx
version: 1.14.0
division: infrastructure
spec:
replicas: 3
selector:
matchLabels:
name: gq-hello-nginx
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
template:
metadata:
labels:
name: gq-hello-nginx
spec:
containers:
- name: gq-hello-nginx
image: __IMAGE__
imagePullPolicy: Always
ports:
- name: gq-hello-nginx
containerPort: 80
protocol: TCP

+ 26
- 0
config/kubernetes/gq-hello-nginx-ingress.yaml View File

@ -0,0 +1,26 @@
# gq-hello-nginx-ingress.yaml
#
# @version: 180627:1
# @author: zhangxuhong <zhangxuhong@xitu.io>
#
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gq-hello-nginx
labels:
name: gq-hello-nginx
role: backend
pl: c
application: nginx
version: 1.14.0
division: infrastructure
spec:
rules:
- host: gq-hello.juejin.im
http:
paths:
- path: /
backend:
serviceName: gq-hello-nginx
servicePort: 80

+ 24
- 0
config/kubernetes/gq-hello-nginx-service.yaml View File

@ -0,0 +1,24 @@
# gq-hello-nginx-service.yaml
#
# @version 180719:1
# @author zhangxuhong <zhangxuhong@xitu.io>
kind: Service
apiVersion: v1
metadata:
name: gq-hello-nginx
labels:
name: gq-hello-nginx
role: backend
pl: c
application: nginx
version: 1.14.0
division: infrastructure
spec:
ports:
- port: 80
targetPort: gq-hello-nginx
protocol: TCP
name: gq-hello-nginx
selector:
name: gq-hello-nginx

+ 24
- 0
config/kubernetes/gq-hello-service.yaml View File

@ -0,0 +1,24 @@
# gq-hello-service.yaml
#
# @version 180719:1
# @author zhangxuhong <zhangxuhong@xitu.io>
kind: Service
apiVersion: v1
metadata:
name: gq-hello
labels:
name: gq-hello
role: backend
pl: c
application: nginx
version: 1.14.0
division: infrastructure
spec:
ports:
- port: 9000
targetPort: gq-hello
protocol: TCP
name: gq-hello
selector:
name: gq-hello

+ 71
- 0
config/nginx/gq-hello.public.conf View File

@ -0,0 +1,71 @@
# gq-hello.public.conf
# gq-hello nginx public config.
# This api is a [public] api.
# @version 170624:8
# server config
server {
listen 80;
server_name gq-hello.juejin.im;
# origin sessings
include /data/apps/nginx/conf/origin/gold.xitu.io_and_juejin.im.conf;
default_type 'text/plain'; # or browser will download page
root /data/repo/gq-hello/src/;
access_log /dev/stdout;
error_log /dev/stderr;
client_body_temp_path /data/tmp/nginx/client_body_temp/ 1 2;
proxy_temp_path /data/tmp/nginx/proxy_temp/ 1 2;
fastcgi_temp_path /data/tmp/nginx/fastcgi_temp/ 1 2;
rewrite "^(.*)/v1/sample$" $1/SampleApi.php?$query_string last;
rewrite "^(.*)/status$" $1/status.php last;
# global location settings
location / {
return 200;
}
location = /ENV {
allow 127.0.0.1;
deny all;
}
location ~* ^/DOCUMENTS{
return 404;
}
location ~* ^/logs{
return 404;
}
location ~* ^/src{
return 404;
}
location ~* ^/config{
return 404;
}
location = /favicon.ico {
allow all;
log_not_found off;
access_log off;
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ \.php$ {
if ( $fastcgi_script_name ~ \..*\/.*php ) {
return 403;
}
include fastcgi.conf;
#这里如果是本地测试,要使用docker的ip,还不能解析名称,要使用容器ip,使用hostname -I
#进入容器要使用docker exec -i -t {dockerId} /bin/sh
#查看dockerId使用命令docker ps
fastcgi_pass gq-hello:9000;
fastcgi_index index.php;
}
}

+ 21
- 0
gq-hello-nginx.dockerfile View File

@ -0,0 +1,21 @@
# gq-hello-nginx.dockerfile
# Dockerfile for demo gq-hello-nginx
# This docker file base on harbor02.juejin.id/lib/php:7.2.9-fpm-alpine3.8
# @version 180719:2
# @author zhangxuhong <zhangxuhong@xitu.io>
#
# base info
FROM harbor02.juejin.id/infrastructure/nginx-1.14.0-centos:latest
MAINTAINER zhangxuhong <zhangxuhong@xitu.io>
USER root
# copy config to /data/apps/nginx/conf/vhost/
COPY ./config/nginx/ /data/apps/nginx/conf/vhost/
# define health check
HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://127.0.0.1:80/status?src=docker_health_check -H"Host:gq-hello.juejin.im" || exit 1
# run php-fpm
EXPOSE 80
ENTRYPOINT ["/data/apps/nginx/sbin/nginx", "-g", "daemon off;"]

+ 25
- 0
gq-hello.dockerfile View File

@ -0,0 +1,25 @@
# gq-hello.dockerfile
# Dockerfile for demo gq-hello
# This docker file base on harbor02.juejin.id/lib/php:7.2.9-fpm-alpine3.8
# @version 180719:2
# @author zhangxuhong <zhangxuhong@xitu.io>
#
# base info
FROM harbor02.juejin.id/infrastructure/php-7.2.9-fpm-alpine3.8:latest
MAINTAINER zhangxuhong <zhangxuhong@xitu.io>
USER root
# init
# RUN apk add --update --no-cache --virtual .build-deps \
# copy repo to /data/repo
COPY . /data/repo/gq-hello/
# define health check
HEALTHCHECK --interval=5s --timeout=3s CMD netstat -an | grep 9000 > /dev/null; if [ 0 != $? ]; then exit 1; fi;
# run php-fpm
EXPOSE 9000
ENTRYPOINT ["php-fpm"]

+ 1
- 0
lock/placeholder.lock View File

@ -0,0 +1 @@
1

+ 0
- 0
logs/.gitkeep View File


+ 18
- 0
src/SampleApi.php View File

@ -0,0 +1,18 @@
<?php
/**
* SampleApi.php
* Api entrance
* @version 151024:1
* @author karminski <code.karminski@outlook.com>
*/
define('PROCESS_NAME', 'index');
define('DEBUG', true);
// controller
require(dirname(__FILE__).'/protected/controller/BaseController.php');
require(dirname(__FILE__).'/protected/controller/SampleController.php');
$SampleController = new SampleController;
$SampleController->run();

+ 50
- 0
src/protected/config/main.config.php View File

@ -0,0 +1,50 @@
<?php
/**
* Main config file.
* @version 161017:1
* @author karminski <code.karminski@outlook.com>
*
*/
/**
* set default ini
*/
ini_set("date.timezone", "Asia/Harbin");
/**
* product config here
*/
$product = array(
'global' => array(
'global_id' => 'salt_fish_raw_php',
'folder' => '/data/repo/salt_fish_raw_php/',
),
'log' => array(
'open' => true,
'address' => '/data/repo/salt_fish_raw_php/logs/', // the '/' at end of line is necessary
'split' => 'day', // options: month, day, hour, minute
),
'storage' => array(
'sample' => array(
'host' => '192.168.0.1',
'port' => 3306,
'name' => 'sample',
'charset' => 'utf8',
'user' => 'sample',
'pass' => 'sample',
),
),
'cache' => array(
'sample_cache' => array(
'host' => '192.168.0.1',
'port' => 6379,
'pass' => null,
'database' => 2,
'timeout' => 0,
),
),
'mongodb' => array(
'sample' => "mongodb://sample:password@192.168.0.1:27017/sample",
),
);

+ 122
- 0
src/protected/controller/BaseController.php View File

@ -0,0 +1,122 @@
<?php
/**
* BaseController.php
* Base controller.
* @version 160823:8
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 160823:8 REMOVE initFeedback method.
* 160129:7 ADD checkShutDownSignal().
* 151123:6 REFACTORY.
* 151024:5 REFACTORY.
* 150824:4 ADD isProcessNumberOverLimit().
* 150811:3 CHANGE input method.
* 150609:2 ADD debugSwitch().
* 150520:1 INIT baseController.
*/
namespace SaltFish;
/**
* abstract class BaseController
*/
abstract class BaseController{
public $_log;
public $_feedback;
/**
* [__construct description]
*/
public function __construct(){
$this->debugSwitch();
$this->initRequirements();
$this->initLog();
}
/**
* [debugSwitch description]
* This function set debug info.
* @param null
* @return null
*/
public function debugSwitch(){
if(!defined(DEBUG)){
define(DEBUG, false);
}
if(DEBUG){
ini_set('display_errors', 1);
error_reporting(E_ALL);
}
}
/**
* [initRequirements description]
* This function load main config and all necessary files.
* @param null
* @return null
*/
public function initRequirements(){
// init config
require(dirname(__FILE__).'/../lib/base/AutoRequire.php');
require(dirname(__FILE__).'/../lib/base/Config.php');
$conf = require(dirname(__FILE__).'/../config/main.config.php');
Config::importMainConfig($conf);
// load all necessary files
$class_name = get_class($this);
$config = AutoRequire::headerArray("{$class_name}.requirements");
AutoRequire::classes($config);
}
/**
* [initLog description]
* This function init log address and echo settings.
* @param null
* @return null
*/
public function initLog(){
$logConfig = Config::get('log');
Log::setLogAddress($logConfig['address'], PROCESS_NAME, $logConfig['split']);
if(DEBUG){
Log::setEchoSwitch(Log::ECHO_THE_LOG);
}
}
/**
* [fetchInput description]
* @param [type] &$inputException [description]
* @return [type] [description]
*/
public function fetchInput(&$inputException){
$inputIsOk = Input::getInputByParams(
$this->input_method, $this->input_param, $this->input, $inputException
);
// check callback
if(!empty($input['callback'])){
$this->output_format = Feedback::FORMAT_JSONP;
}
// gen feeedback
if(!$inputIsOk) return false;
return true;
}
/**
* [checkShutDownSignal description]
* @param [type] $signal [description]
* @return [type] [description]
*/
public static function checkShutDownSignal($signal, $shutdownMessage = 'signal file removed'){
if(file_exists($signal)) return;
Log::message('checkShutDownSignal', 'DETECTED', 'EXIT', $shutdownMessage);
exit("signal file removed, process shut down.\n");
}
/**
* [run description]
* @return [type] [description]
*/
public abstract function run();
}

+ 110
- 0
src/protected/controller/SampleController.php View File

@ -0,0 +1,110 @@
<?php
/**
* SampleController.php
* SampleController controller.
* @version 160901:1
* @author karminski karminski@outlook.com>
*
* @changes
* 160901:1 INIT version.
*/
/**
* class SampleController extends BaseController
*/
class SampleController extends SaltFish\BaseController{
private $_h;
public $input = array(); // user input data
public $default_input_method = '';
public $output_format = '';
public $input_param = array();
/**
* [__construct description]
*/
public function __construct(){
parent::__construct();
}
/**
* public function setInputRules
*/
public function setInputRules(){
$this->default_input_method = SaltFish\Input::METHOD_GET;
$this->output_format = SaltFish\Feedback::FORMAT_JSON;
$this->input_param = array(
array(
'param' => 'uid',
'condition' => array(
'method' => null,
'type' => SaltFish\Input::TYPE_INT,
'limit' => array(),
'default' => 0,
'necessary' => true,
),
),
);
SaltFish\Feedback::setOutputFormat($this->output_format);
SaltFish\Feedback::setCallback(null);
}
/**
* [initDatabase description]
* @return [type] [description]
*/
public function initDatabase(){
$this->SampleCache = new SampleCache;
$this->SampleMongoDBStorage = new SampleMongoDBStorage;
$this->SampleMySQLStorage = new SampleMySQLStorage;
}
/**
* [run description]
* @return [type] [description]
*/
public function run(){
// init
$Log = $this->_log;
// get user input
$exception = array();
$this->setInputRules();
if(!SaltFish\Input::get($this->default_input_method, $this->input_param, $this->input, $exception)){
$feed = SaltFish\Feedback::getFeed(FeedbackInfo::S_WRONG_INPUT, $exception, array());
BaseView::renderJson($feed);
return false;
}
// init request, result
$SampleRequest = new SampleRequest($this->input);
$SampleResult = new SampleResult;
// check access token
// if(SaltFish\AccessibilityCheck::isIllegalToken($SampleRequest)){
// $feed = SaltFish\Feedback::getFeed(FeedbackInfo::S_WRONG_INPUT, FeedbackInfo::M_ILLEGAL_TOKEN, array());
// BaseView::renderJson($feed);
// return false;
// }
// init database
$this->initDatabase();
// sample 1 : get data from cache
if(!$this->SampleCache->get($SampleRequest, $SampleResult)){
$feed = SaltFish\Feedback::getFeed(FeedbackInfo::S_NO_RESULT, FeedbackInfo::M_NO_RESULT, array());
BaseView::renderJson($feed);
return false;
}
// ok, feedback
$feedData = array();
$SampleResult->exportForFeedback($feedData);
$feed = SaltFish\Feedback::getFeed(FeedbackInfo::S_OK, FeedbackInfo::M_OK, $feedData);
BaseView::renderJson($feed);
return true;
}
}

+ 48
- 0
src/protected/h/SampleController.requirements.h.php View File

@ -0,0 +1,48 @@
<?php
/**
* SampleController.requirements.h.php
* @version 150831:1
* @author karminski <code.karminski@outlook.com>
*
*/
return array(
'config' => array(
'main.config'
),
'lib' => array(
'base/Log',
'base/Feedback',
'base/Input',
'base/AccessibilityCheck',
'network/Curl',
'encrypt/Encrypt',
'cache/RedisFactory',
'cache/RedisProxy',
'storage/File',
'storage/FluentPDO',
'storage/MysqlFactory',
'storage/MysqlProxy',
'storage/MongoDBFactory',
),
'controller' => array(
),
'model' => array(
'FeedbackInfo',
'Request',
'Result',
'ApiProxy',
'SampleCache',
'SampleMongoDBStorage',
'SampleMySQLStorage',
'SampleApiProxy',
'SampleRequest',
'SampleResult',
),
'views' => array(
'BaseView',
),
);

+ 13
- 0
src/protected/h/public_key.h.php View File

@ -0,0 +1,13 @@
<?php
/**
* public_key.h.php
* @version 151024:2
* @author karminski <code.karminski@outlook.com>
*
*/
return array(
"default" => "PV0XRWUebytdO8c9QhET",
"tester" => "kvYgJ19P3DW4mRcfNO0u",
"example" => "GBk3UAbh5SefRZsnwj2T",
);

+ 256
- 0
src/protected/lib/algorithm/TrieTree.php View File

@ -0,0 +1,256 @@
<?php
/**
* TrieTree.php
* Algorithm Trie-tree php implementation.
* @version 150609:4
* @author karminski <code.karminski@outlook.com>
*
* @changes
* 150609:4 fix reference issue, ADD importTree() retval.
* 150528:3 add subContain().
*/
/**
* class TrieTree
*/
class TrieTree{
// const
const SHORT_SEARCH = true;
const NOT_SHORT_SEARCH = false;
// properties
public $tree = array();
/**
* [flushTree description]
* @return [type] [description]
*/
public function flushTree(){
$this->tree = array();
}
/**
* [importTree description]
* @param [type] $tree [description]
* @return [type] [description]
*/
public function importTree(&$tree){
$this->tree = $tree;
if(empty($this->tree)) return false;
return true;
}
/**
* [exportTree description]
* @param [type] $tree [description]
* @return [type] [description]
*/
public function exportTree(){
return $this->tree;
}
/**
* [insertAll description]
* @param [type] $data [description]
* @return [type] [description]
*/
public function insertAll($data){
foreach($data as $line){
$this->insert($line['word'], explode(",", $line['type']));
}
}
/**
* [insert description]
* @param [type] $utf8_str [description]
* @return [type] [description]
*/
public function insert($utf8_str = "", $type = array()){
// unset empty type
foreach($type as $serial => $line){
if(empty($line)){
unset($type[$serial]);
}
}
// insert tree
$chars = String::utf8Split(trim($utf8_str));
$chars[] = null; // null for end of thread
$count = count($chars);
$T = &$this->tree;
$last = $count-1;
for($i = 0;$i < $count;$i++){
$c = $chars[$i];
if(!array_key_exists($c, $T)){
$T[$c] = array(); // insert new char
}
// fill type
if($i === $last){
$T['__type'] = $type;
}
$T = &$T[$c];
}
}
/**
* [remove description]
* @param [type] $utf8_str [description]
* @return [type] [description]
*/
public function remove($utf8_str){
$chars = String::utf8Split($utf8_str);
$chars[] = null;
if($this->_find($chars)){ // match the head char
$chars[] = null;
$count = count($chars);
$T = &$this->tree;
for($i = 0;$i < $count;$i++){
$c = $chars[$i];
if(count($T[$c]) == 1){ // only this thread
unset($T[$c]);
return;
}
$T = &$T[$c];
}
}
}
/**
* [_find description]
* @param [type] &$chars [description]
* @return [type] [description]
*/
private function _find(&$chars){
$string = "";
$count = count($chars);
$T = &$this->tree;
for($i = 0;$i < $count;$i++){
$c = $chars[$i];
if(empty($c)){
return explode(",", $this->recrusiveStr($T, $string));
}
$T = &$T[$c];
$string .= $c;
}
return $string;
}
/**
* [recrusiveStr description]
* @param [type] $tree [description]
* @param [type] $results [description]
* @return [type] [description]
*/
public function recrusiveStr($tree, $results){
if(empty($tree)) return false;
$r = "";
foreach($tree as $key => $subTree){
$results .= $key;
if(empty($subTree)) return $results.",";
$r .= $this->recrusiveStr($subTree, $results);
}
return $r;
}
/**
* [find description]
* @param [type] $utf8_str [description]
* @return [type] [description]
*/
public function find($utf8_str){
$chars = String::utf8Split($utf8_str);
$chars[] = null;
return $this->_find($chars);
}
/**
* [subContain description]
* @param [type] &$chars [description]
* @param [type] $len [description]
* @param [type] &$Tree [description]
* @param [type] &$hit_tmp [description]
* @param [type] &$hit_words [description]
* @param [type] $short_search [description]
* @return [type] [description]
*/
private static function subContain(&$chars, $len, &$Tree, &$hit_tmp, &$hit_words, $short_search){
for($i = 0; $i<$len; $i++){
$c = $chars[$i];
if(array_key_exists($c, $Tree)){
$T = &$Tree[$c];
for($j = $i + 1;$j < $len;$j++){
$c = $chars[$j];
$hit_tmp[] = $chars[$j-1];
// if end match
if(array_key_exists(null, $T)){
$hitWord = implode("", $hit_tmp);
if(!empty($hit_words[$hitWord])){
$hit_words[$hitWord]['hits'] ++;
}else{
$hit_words[$hitWord] = array(
'hits' => 1,
'type' => $T['__type'],
);
}
if($short_search){
$hit_tmp = array();
return true;
}
}
// if wildcard
if(array_key_exists(" ", $T)){
$T = &$T[" "];
$hit_tmp[] = " ";
self::subContain($chars, $len, $T, $hit_tmp, $hit_words, true);
$hit_tmp = array();
continue;
}
// if miss match
if(!array_key_exists($c, $T)){
array_pop($hit_tmp);
break;
}
$T = &$T[$c];
}
}
}
}
/**
* [contain description]
* @param [type] $utf8_str [description]
* @param integer $do_count [description]
* @return [type] [description]
*/
public function contain($utf8_str, &$hit_words = array(), $short_search = false){
$chars = String::utf8Split($utf8_str);
$chars[] = null;
$len = count($chars);
$Tree = &$this->tree;
$count = 0;
$totalLoop = 0;
$hit_tmp = array();
self::subContain($chars, $len, $Tree, $hit_tmp, $hit_words, $short_search);
if(!empty($hit_words)){
return true;
}else{
return false;
}
}
/**
* [containAll description]
* @param [type] $str_array [description]
* @return [type] [description]
*/
public function containAll($str_array){
foreach($str_array as $str){
if($this->contain($str)){
return true;
}
}
return false;
}
}

+ 132
- 0
src/protected/lib/base/AccessibilityCheck.php View File

@ -0,0 +1,132 @@
<?php
/**
* AccessibilityCheck.php
* Accessibility check model.
* @version 160908:4
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 160908:4 ADD generatePayload().
* 160823:3 CHANGE string serialize method.
* 151024:2 REFACTORY
* 151020:1 INIT version.
*/
namespace SaltFish;
/**
* class AccessibilityCheck
*/
class AccessibilityCheck{
const PUBLIC_KEY_FILE = 'public_key';
const DEFAULT_PUBLIC_KEY = 'default';
const DEFAULT_EXPIRE = 3; // minutes
private static $instance;
private $publicKey = "";
/**
* [__construct description]
*/
private function __construct(){
$this->loadHeaderFile();
}
/**
* [getInstance description]
* @param null
* @return Log $instance
*/
public static function getInstance(){
if(empty(self::$instance)){
self::$instance = new AccessibilityCheck;
}
return self::$instance;
}
/**
* [loadHeaderFile description]
* @return [type] [description]
*/
public function loadHeaderFile(){
$this->publicKey = AutoRequire::headerArray(self::PUBLIC_KEY_FILE);
return true;
}
/**
* [getPublicKey description]
* @param [type] $src [description]
* @return [type] [description]
*/
public static function getPublicKey($src){
$instance = self::getInstance();
if(empty($instance->publicKey[$src])) return $instance->publicKey[self::DEFAULT_PUBLIC_KEY];
return $instance->publicKey[$src];
}
/**
* [generatePayload description]
* @param [type] $data [description]
* @param [type] $public_key [description]
* @param integer $expire [description]
* @return [type] [description]
*/
public static function generatePayload($data, $public_key, $expire = self::DEFAULT_EXPIRE){
ksort($data);
$str = "";
$str .= json_encode($data);
$str .= $public_key;
$payloads = array();
if(empty($expire)){
return array(
0 => $str,
);
}
$start = -floor($expire * 0.5);
for($i=$start;$i<=-$start;$i++){
if($i!==0){
$payloads[] = $str.strtotime(date("Y-m-d H:i:00", strtotime("{$i} minute")));
}else{
$payloads[] = $str.strtotime(date("Y-m-d H:i:00"));
}
}
return $payloads;
}
/**
* [generateToken description]
* @param [type] $data [description]
* @param [type] $public_key [description]
* @return [type] [description]
*/
public static function generateToken($data, $public_key){
ksort($data);
$str = "";
$str .= json_encode($data);
$str .= $public_key;
$str .= strtotime(date("Y-m-d H:i:00"));
return Encrypt::hash($str);
}
/**
* [isIllegalToken description]
* @param Request $Request [description]
* @return boolean [description]
*/
public static function isIllegalToken(\Request $Request){
$instance = self::getInstance();
$data = array();
$token = "";
$src = "";
$Request->exportForAccessibilityCheck($data);
$token = $Request->getToken();
$src = $Request->getSrc();
$payloads = self::generatePayload($data, self::getPublicKey($src));
foreach($payloads as $payload){
if(Encrypt::matchMd5($payload, $token)) return false;
}
return true;
}
}

+ 67
- 0
src/protected/lib/base/AutoRequire.php View File

@ -0,0 +1,67 @@
<?php
/**
* AutoRequire.php
* auto require lib.
* @version 151123:3
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 151123:3 CHNAGE function name.
* 150609:2 FIX _header_array(), _header_json() require file name issue.
* 150520:1 INIT commit.
*/
namespace SaltFish;
/**
* CLASS AutoRequire
*/
class AutoRequire{
/**
* [basedir description]
* @param null
* @return string basedir
*/
private static function basedir(){
return dirname(__FILE__).'/../../';
}
/**
* [classes description]
* @param array $classes, required class type and name
* @return null
*/
public static function classes(
$classes = array('type' => array('name_1', 'name_2'))
){
$basedir = self::basedir();
foreach($classes as $type => $names){
if(empty($names)) continue;
foreach($names as $name){
require("{$basedir}{$type}/{$name}.php");
}
}
}
/**
* [headerArray description]
* @param string $name, config name
* @return array h, in file array
*/
public static function headerArray($name = ''){
$basedir = self::basedir();
return require("{$basedir}h/{$name}.h.php");
}
/**
* [headerJson description]
* @param string $name, config name
* @return array h, in file array
*/
public static function headerJson($name = ''){
$basedir = self::basedir();
return json_decode(require("{$basedir}h/{$name}.h.json"), true);
}
}

+ 72
- 0
src/protected/lib/base/Config.php View File

@ -0,0 +1,72 @@
<?php
/**
* Config.php
* For config fetch.
* @version 151123:1
* @author karminski <code.karminski@outlook.com>
*
* @changes
* 151123:1 INIT commit.
*/
namespace SaltFish;
/**
* CLASS Config
*/
class Config{
private static $instance;
private $main;
private $h;
private function __construct(){
}
/**
* [importMainConfig description]
* @param array $main, main config array
* @return null
*/
public static function importMainConfig(&$main){
$instance = self::getInstance();
$instance->main = &$main;
}
/**
* [get description]
* @param string $field_name, main config field name
* @return array config
*/
public static function get($field_name = ''){
$instance = self::getInstance();
return $instance->main[$field_name];
}
/**
* [getHeader description]
* @param string $name, header file name
* @return array h, header array
*/
public static function getHeader($name = ''){
$instance = self::getInstance();
if(empty($instance->h[$name])){
$instance->h[$name] = AutoRequire::headerArray($name);
}
return $instance->h[$name];
}
/**
* [getInstance description]
* @param null
* @return Config $instance
*/
public static function getInstance(){
if(empty(self::$instance)){
self::$instance = new Config;
}
return self::$instance;
}
}

+ 150
- 0
src/protected/lib/base/Feedback.php View File

@ -0,0 +1,150 @@
<?php
/**
* Feedback.php
* Feedback message.
* @version 160823:10
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 160823:10 CHANGE to singleton.
* 151024:9 REFACTORY.
* 150811:8 ADD raw result format.
* 150520:7 old version.
*
*/
namespace SaltFish;
/**
* class Feedback
*/
class Feedback{
// const
const SIGNAL = 's';
const MESSAGE = 'm';
const DATA = 'd';
const M_OK = 'ok';
const FORMAT_RAW = 'raw';
const FORMAT_JSON = 'json';
const FORMAT_JSONP = 'jsonp';
const FORMAT_SERIALIZE = 'serialize';
private static $instance;
public $output_format = self::FORMAT_JSON;
public $callback = '';
public static $result_tmpl = array(
self::SIGNAL => false,
self::MESSAGE => self::M_OK,
self::DATA => array(),
);
/**
* [__construct description]
*/
private function __construct(){
return;
}
/**
* [getInstance description]
* @param null
* @return Log $instance
*/
public static function getInstance(){
if(empty(self::$instance)){
self::$instance = new Feedback;
}
return self::$instance;
}
/**
* [setOutputFormat description]
* @param [type] $output_format [description]
*/
public static function setOutputFormat($output_format = self::FORMAT_JSON){
$instance = self::getInstance();
$instance->output_format = $output_format;
return;
}
/**
* [setCallback description]
* @param string $callback [description]
*/
public static function setCallback($callback = ''){
$instance = self::getInstance();
$instance->callback = $callback;
return;
}
/**
* [resultTransfer description]
* You can define your return format status field in here
* @param [type] $result [description]
* @return [type] [description]
*/
private static function resultTransfer($result){
if(is_numeric($result)) return $result;
if(!empty($result)){
return true;
}else{
return false;
}
}
/**
* [returnFromat description]
* this method leave format param for extension on future
* @param [type] $data [description]
* @param [type] $format [description]
* @param string $callback [description]
* @return [type] [description]
*/
private static function returnFromat(
&$data, $format = self::FORMAT_JSON, $callback = ''
){
if($format === self::FORMAT_JSON){
return json_encode($data);
}elseif($format === self::FORMAT_JSONP||!empty($callback)){
$data = json_encode($data);
$callback = urlencode($callback);
return "{$callback}($data)";
}elseif($format === self::FORMAT_SERIALIZE){
return serialize($data);
}elseif($format === self::FORMAT_RAW){
return $data;
}
}
/**
* [getFeed description]
* @param boolean $result [description]
* @param array $message [description]
* @param array $data [description]
* @param [type] $format [description]
* @param string $callback [description]
* @return [type] [description]
*/
public static function getFeed(
$result = false,
$message = array(),
$data = array()
){
$instance = self::getInstance();
$result = self::resultTransfer($result);
$r = self::$result_tmpl;
$r[self::SIGNAL] = $result;
if(!empty($message)){
$r[self::MESSAGE] = $message;
}
if(!empty($data)){
$r[self::DATA] = &$data;
}
return self::returnFromat($r, $instance->output_format, $instance->callback);
}
}

+ 155
- 0
src/protected/lib/base/Input.php View File

@ -0,0 +1,155 @@
<?php
/**
* Input.php
* Base input class.
* @version 160204:11
* @author karminski <code.karminski@outlook.com>
*/
namespace SaltFish;
/**
* class Input
*/
class Input{
// input method
const METHOD_GET = 'get';
const METHOD_POST = 'post';
const METHOD_ARGV = 'argv';
const METHOD_HTTP = 'http';
// input types
const TYPE_INT = 'int';
const TYPE_STRING = 'string';
const TYPE_ENUM = 'enum';
const TYPE_ARRAY = 'array';
const TYPE_BOOLEAN = 'array';
/**
* [getInputByMethod description]
* get and fill default value when input empty.
* @param [type] $method [description]
* @param [type] $param [description]
* @param [type] $default [description]
* @return mixed, input data
*/
private static function getInputByMethod($method, $param, $default){
if($method === self::METHOD_GET){
return empty($_GET[$param]) ? $default : $_GET[$param];
}elseif($method === self::METHOD_POST){
return empty($_POST[$param]) ? $default : $_POST[$param];
}elseif($method === self::METHOD_HTTP){
$post = empty($_POST[$param]) ? null : $_POST[$param];
if(empty($post)){
return empty($_GET[$param]) ? $default : $_GET[$param];
}
return $post;
}elseif($method === self::METHOD_ARGV){
$argvInput = getopt(false, array("{$param}:"));
return empty($argvInput[$param]) ? $default : $argvInput[$param];
}
}
/**
* [isMissing description]
* @param [type] &$data [description]
* @param [type] $necessary [description]
* @return boolean [description]
*/
private static function isMissing(&$data, $necessary){
if($necessary && !isset($data)) return true;
return false;
}
/**
* [isIllegal description]
* @param [type] &$data [description]
* @param [type] &$condition [description]
* @return boolean [description]
*/
private static function isIllegal(&$data, &$condition){
$type = $condition['type'];
if($type === self::TYPE_INT){
$data = (int)$data;
if(!empty($condition['limit'])){
if(isset($condition['limit']['min'])){
if($data < $condition['limit']['min']) return true;
}
if(isset($condition['limit']['max'])){
if($data > $condition['limit']['max']) return true;
}
}
}elseif($type === self::TYPE_STRING){
if(!empty($condition['limit'])){
$len = strlen($data);
if(isset($condition['limit']['min'])){
if($len < $condition['limit']['min']) return true;
}
if(isset($condition['limit']['max'])){
if($len > $condition['limit']['max']) return true;
}
}
}elseif($type === self::TYPE_ENUM){
if(!empty($condition['options'])){
$enums = array();
foreach($condition['options'] as $avaliable_value){
$enums[$avaliable_value] = true;
}
if(empty($enums[$data])) return true;
}
}elseif($type === self::TYPE_ARRAY){
$subCondition = $condition;
$subCondition['type'] = $condition['sub_type'];
if(self::isIllegal($line, $subCondition)) return true;
}elseif($type === self::TYPE_BOOLEAN){
if(!isset($ata)) return true;
}
return false;
}
/**
* [get description]
* @param [type] $default_input_method [description]
* @param array &$input_param [description]
* @param array &$result [description]
* @param array &$exception [description]
* @return [type] [description]
*/
public static function get(
$default_input_method = self::METHOD_HTTP,
&$input_param = array(),
&$result = array(),
&$exception = array()
){
$isMissing = false;
$isIllegal = false;
$exception = array(
'missing' => array(),
'illegal' => array(),
);
foreach($input_param as $line){
$param = &$line['param'];
$condition = &$line['condition'];
// check method
$method = empty($condition['method']) ? $default_input_method : $condition['method'];
// get and fill default value when input empty.
$result[$param] = self::getInputByMethod($method, $param, $condition['default']);
// check necessary
if(self::isMissing($result[$param], $condition['necessary'])){
$isMissing = true;
$exception['missing'] = $param;
}
// check illegal
if(self::isIllegal($result[$param], $condition)){
$isIllegal = true;
$exception['illegal'] = $param;
}
}
if($isMissing||$isIllegal) return false;
return true;
}
}

+ 198
- 0
src/protected/lib/base/Log.php View File

@ -0,0 +1,198 @@
<?php
/**
* Log.php
* Log something.
* @version 151231:8
* @author karminski <code.karminski@outlook.com>
*
* @changelogs 151231:8 ADD writeRaw().
* 151123:7 ADD getInstance().
* 151024:6 RENAME.
* 150831:5 ADD process name.
* 150826:4 ADD uniqid
* 150609:3 ADD echoSwitch().
* 140626:2 INIT base log.
*
* @example
*
* exec:
* Log::error("somefunction()", "failed to exec", "jump", array('return' => false));
* then will write to log:
* 0000-00-00 00:00:00 <-ERROR-> [somefunction()] [failed to exec] [jump] return: false \n
*/
namespace SaltFish;
/**
* class Log
*/
class Log{
const SIGNAL_OK = ' O K ';
const SIGNAL_BAD = ' BAD ';
const SIGNAL_MESSAGE = 'MESSAGE';
const SIGNAL_WARING = 'WARING!';
const SIGNAL_ERROR = '-ERROR-';
const SIGNAL_FATAL = '-FATAL-';
const ECHO_THE_LOG = true;
const DO_NOT_ECHO_THE_LOG = false;
private static $instance;
private $logAddress = '';
private $echoSwitch = false;
private function __construct(){
}
/**
* [getInstance description]
* @param null
* @return Log $instance
*/
public static function getInstance(){
if(empty(self::$instance)){
self::$instance = new Log;
}
return self::$instance;
}
/**
* [setLogAddress description]
* @param string $addr
* @param string $logName
* @param string $split, log split method
*/
public static function setLogAddress($addr = '', $logName = '', $split = ''){
$instance = self::getInstance();
if($split === 'year'){
$suffix = date("Y");
}elseif($split === 'month'){
$suffix = date("Ym");
}elseif($split === 'day'){
$suffix = date("Ymd");
}elseif($split === 'hour'){
$suffix = date("YmdH");
}elseif($split === 'minute'){
$suffix = date("YmdHi");
}elseif($split === 'second'){
$suffix = date("YmdHis");
}else{
$suffix = '';
}
$instance->logAddress = $addr.$logName.".".$suffix.".".uniqid().".log";
}
/**
* [setEchoSwitch description]
* @param boolean $switch, set true to print log
*/
public static function setEchoSwitch($switch = false){
$instance = self::getInstance();
$instance->echoSwitch = $switch;
}
/**
* [writeLog description]
* @param [type] $signal [description]
* @param string $action [description]
* @param string $ret_info [description]
* @param string $process [description]
* @param array $dump [description]
* @return [type] [description]
*/
private static function writeLog($signal, $action = '', $ret_info = '', $process = '', $dump = array()){
$instance = self::getInstance();
$timestamp = date("Y-m-d H:i:s");
$dump = serialize($dump);
error_log("{$timestamp} <{$signal}>\t[{$action}]\t[{$ret_info}][{$process}] {$dump} \n", 3, $instance->logAddress);
if($instance->echoSwitch){
echo("{$timestamp} <{$signal}>\t[{$action}]\t[{$ret_info}][{$process}] {$dump} \n");
}
}
/**
* [writeRaw description]
* @param [type] &$line [description]
* @return [type] [description]
*/
public static function writeRaw(&$line){
$instance = self::getInstance();
error_log($line+"\n", 3, $instance->logAddress);
}
/**
* [ok description]
* @param string $action [description]
* @param string $ret_info [description]
* @param string $process [description]
* @param array $dump [description]
* @return [type] [description]
*/
public static function ok($action = '', $ret_info = '', $process = '', $dump = array()){
self::writeLog(self::SIGNAL_OK , $action, $ret_info, $process, $dump);
}
/**
* [bad description]
* @param string $action [description]
* @param string $ret_info [description]
* @param string $process [description]
* @param array $dump [description]
* @return [type] [description]
*/
public static function bad($action = '', $ret_info = '', $process = '', $dump = array()){
self::writeLog(self::SIGNAL_BAD , $action, $ret_info, $process, $dump);
}
/**
* [message description]
* @param string $action [description]
* @param string $ret_info [description]
* @param string $process [description]
* @param array $dump [description]
* @return [type] [description]
*/
public static function message($action = '', $ret_info = '', $process = '', $dump = array()){
self::writeLog(self::SIGNAL_MESSAGE , $action, $ret_info, $process, $dump);
}
/**
* [waring description]
* @param string $action [description]
* @param string $ret_info [description]
* @param string $process [description]
* @param array $dump [description]
* @return [type] [description]
*/
public static function waring($action = '', $ret_info = '', $process = '', $dump = array()){
self::writeLog(self::SIGNAL_WARING , $action, $ret_info, $process, $dump);
}
/**
* [error description]
* @param string $action [description]
* @param string $ret_info [description]
* @param string $process [description]
* @param array $dump [description]
* @return [type] [description]
*/
public static function error($action = '', $ret_info = '', $process = '', $dump = array()){
self::writeLog(self::SIGNAL_ERROR , $action, $ret_info, $process, $dump);
}
/**
* [fatal description]
* @param string $action [description]
* @param string $ret_info [description]
* @param string $process [description]
* @param array $dump [description]
* @return [type] [description]
*/
public static function fatal($action = '', $ret_info = '', $process = '', $dump = array()){
self::writeLog(self::SIGNAL_FATAL , $action, $ret_info, $process, $dump);
exit();
}
}

+ 130
- 0
src/protected/lib/base/ObjectArray.php View File

@ -0,0 +1,130 @@
<?php
/**
* ObjectArray.php
* ObjectArray class, operate Object as array, implement by SPL.
* @version 151201:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 151201:1 INIT version.
*/
namespace SaltFish;
/**
* abstract class ObjectArray implements ArrayAccess, Iterator
*/
abstract class ObjectArray implements ArrayAccess, Iterator{
private $valid = false;
private $data;
/**
* [__construct description]
* @param array &$data [description]
*/
public function __construct(&$data = array()){
$this->import($data);
}
/**
* [import description]
* @param array &$data [description]
* @return [type] [description]
*/
public function import(&$data = array()){
$this->data = $data;
}
/**
* [exportRaw description]
* @param [type] &$data [description]
* @return [type] [description]
*/
public function exportRaw(&$data){
$data = $this->data;
}
/**
* [offsetSet description]
* @param [type] $offset [description]
* @param [type] $value [description]
* @return [type] [description]
*/
public function offsetSet($offset, $value){
if(is_null($offset)){
$this->data[] = $value;
}else{
$this->data[$offset] = $value;
}
}
/**
* [offsetExists description]
* @param [type] $offset [description]
* @return [type] [description]
*/
public function offsetExists($offset){
return isset($this->data[$offset]);
}
/**
* [offsetUnset description]
* @param [type] $offset [description]
* @return [type] [description]
*/
public function offsetUnset($offset){
unset($this->data[$offset]);
}
/**
* [offsetGet description]
* @param [type] $offset [description]
* @return [type] [description]
*/
public function offsetGet($offset){
return isset($this->data[$offset]) ? $this->data[$offset] : null;
}
/**
* [rewind description]
* @return [type] [description]
*/
public function rewind(){
$this->valid = (false !== reset($this->data));
}
/**
* [current description]
* @return [type] [description]
*/
public function current(){
return current($this->data);
}
/**
* [key description]
* @return [type] [description]
*/
public function key(){
return key($this->data);
}
/**
* [next description]
* @return function [description]
*/
public function next(){
$this->valid = (false !== next($this->data));
}
/**
* [valid description]
* @return [type] [description]
*/
public function valid(){
return $this->valid;
}
}

+ 3484
- 0
src/protected/lib/cache/Predis_Client.php
File diff suppressed because it is too large
View File


+ 59
- 0
src/protected/lib/cache/RedisFactory.php View File

@ -0,0 +1,59 @@
<?php
/**
* RedisFactory.php
* Redis connection factory.
* @version 151025:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 151025:1 INIT version.
*/
namespace SaltFish;
/**
* class RedisFactory
*/
class RedisFactory{
private static $instance;
public $h;
public $pool = array();
/**
* [__construct description]
* @param array &$h, redis all config
*/
private function __construct(){
$this->h = Config::get('cache');
return;
}
/**
* [getInstance description]
* @return [type] [description]
*/
public static function getInstance(){
if(empty(self::$instance)){
self::$instance = new RedisFactory;
}
return self::$instance;
}
/**
* [spawn description]
* @param string $name, redis instance name
* @return RedisProxy RedisProxy
*/
public static function &spawn($name){
$instance = self::getInstance();
// if alerady spawn return pool result
if(!empty($instance->pool[$name])) return $instance->pool[$name];
// spawn
$instance->pool[$name] = new RedisProxy($instance->h[$name]);
return $instance->pool[$name];
}
}

+ 104
- 0
src/protected/lib/cache/RedisProxy.php View File

@ -0,0 +1,104 @@
<?php
/**
* RedisProxy.php
* Redis Proxy for redis connection.
* @version 150105:2
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 141109:1 INIT version.
*
*/
namespace SaltFish;
/**
* CLASS RedisProxy
*/
class RedisProxy{
public $RedisInstance;
public $connectInfo;
/**
* [__construct description]
* @param array &$connectInfo, e.g. array(host=>'', port=>3306, pass=>'', database=>0).
* @return boolean connect result.
*/
public function __construct(&$connectInfo){
$this->connectInfo = $connectInfo;
$this->RedisInstance = new \Redis;
return;
}
/**
* [__destruct description]
*/
public function __destruct(){
return $this->disconnect();
}
/**
* [__call description]
* @param string $name, called function name
* @param array $args, input args
* @return mixed function call return
*/
public function __call($name, $args){
// check connection
if(!$this->connect()) return false;
// call instance
$argnum = count($args);
if($argnum === 1){
return $this->RedisInstance->$name($args[0]);
}elseif($argnum === 2){
return $this->RedisInstance->$name($args[0], $args[1]);
}elseif($argnum === 3){
return $this->RedisInstance->$name($args[0], $args[1], $args[2]);
}elseif($argnum === 4){
return $this->RedisInstance->$name($args[0], $args[1], $args[2], $args[3]);
}
return false;
}
/**
* [getName description]
* @return string name
*/
public function getName(){
return $this->connectInfo['name'];
}
/**
* [connect description]
* @return boolean success
*/
public function connect(){
$info = &$this->connectInfo;
if(!$this->RedisInstance->IsConnected()){
if(!$this->RedisInstance->connect(
$info['host'], $info['port'], $info['timeout']
)) return false;
// auth and select database
if(!empty($info['pass'])){
if(!$this->RedisInstance->auth($info['pass'])) return false;
}
return $this->RedisInstance->select($info['database']);
}
return true;
}
/**
* [disconnect description]
* @return boolean result
*/
public function disconnect(){
if($this->RedisInstance->IsConnected()){
return $this->RedisInstance->close();
}
return true;
}
}

+ 159
- 0
src/protected/lib/encrypt/Encrypt.php View File

@ -0,0 +1,159 @@
<?php
/**
* Encrypt.php
* This class inlcude some encrypt & decrypt method.
* @version 161017:6
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 161017:6 ADD matchMd5() method.
* 160907:5 ADD const.
* 160901:4 ADD getRamdomString() method.
* 160823:3 MAKE iv readable.
* 160130:2 ADD opensslRandomPseudoBytes();
* 141118:1 ADD hash(), match(), pbkdf2() method.
*
*/
namespace SaltFish;
/**
* CLASS Encrypt
*/
class Encrypt{
// consts
const HASH_ALGORITHM = "SHA512";
const IV_LEN = 16;
const HASH_COUNT = 1024;
const OUTPUT_KEY_LEN = 64;
const MD5_LEN = 32;
const MD5_KEY_LEN = 48;
const IV_SOURCE = MCRYPT_DEV_URANDOM;
const MCRYPT_MODE = MCRYPT_MODE_CFB;
const MCRYPT_SIZE = MCRYPT_CAST_256;
/**
* [getRamdomString description]
* @param [type] $len [description]
* @return [type] [description]
*/
public static function getRamdomString($len){
$size = mcrypt_get_iv_size(self::MCRYPT_SIZE, self::MCRYPT_MODE);
return substr(bin2hex(mcrypt_create_iv($size, self::IV_SOURCE)), 0, $len);
}
/**
* [hash description]
* @param [type] $_i [description]
* @return [type] [description]
*/
public static function hash($_i, $iv = ""){
if(empty($iv)){
$iv = self::getRamdomString(self::IV_LEN);
}
$hash = self::pbkdf2(self::HASH_ALGORITHM, $_i, $iv, self::HASH_COUNT, self::OUTPUT_KEY_LEN);
return $hash.$iv;
}
/**
* [match description]
* @param [type] $_i [description]
* @param [type] $_hash [description]
* @return [type] [description]
*/
public static function match($_i, $_hash){
$hash = substr($_hash, 0, self::OUTPUT_KEY_LEN-self::IV_LEN);
$iv = substr($_hash, self::OUTPUT_KEY_LEN-self::IV_LEN);
$newHash = self::pbkdf2(self::HASH_ALGORITHM, $_i, $iv, self::HASH_COUNT, self::OUTPUT_KEY_LEN);
if($hash === $newHash){
return true;
}
return false;
}
/**
* [matchMd5 description]
* @param [type] $_i [description]
* @param [type] $_hash [description]
* @return [type] [description]
*/
public static function matchMd5($_i, $_hash){
$hash = substr($_hash, 0, self::MD5_KEY_LEN-self::IV_LEN);
$iv = substr($_hash, self::MD5_KEY_LEN-self::IV_LEN);
$newHash = md5($_i.$iv);
if($hash === $newHash){
return true;
}
return false;
}
/**
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
private static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false){
$key_length = $key_length - self::IV_LEN; // iv len is usually 16
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
if($count <= 0 || $key_length <= 0)
trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
if(function_exists("hash_pbkdf2")){
// The output length is in NIBBLES (4-bits) if $raw_output is false!
if (!$raw_output) {
// $key_length = $key_length * 2;
}
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if($raw_output){
return substr($output, 0, $key_length);
}else{
return bin2hex(substr($output, 0, $key_length));
}
}
/**
* [opensslRandomPseudoBytes description]
* @param [type] $length [description]
* @return [type] [description]
*/
public static function opensslRandomPseudoBytes($length, $raw_output = false) {
$length_n = (int) $length; // shell injection is no fun
if(!$raw_output) $length_n = $length_n/2;
$handle = popen("/usr/bin/openssl rand $length_n", "r");
$data = stream_get_contents($handle);
pclose($handle);
if($raw_output) return $data;
return bin2hex($data);
}
}

+ 7
- 0
src/protected/lib/encrypt/Encrypt.test.php View File

@ -0,0 +1,7 @@
<?php
require("Encrypt.php");
$r = SaltFish\Encrypt::opensslRandomPseudoBytes(16);
var_dump($r);

+ 1393
- 0
src/protected/lib/html/simple_html_dom.php
File diff suppressed because it is too large
View File


+ 97
- 0
src/protected/lib/network/Curl.php View File

@ -0,0 +1,97 @@
<?php
/**
* Curl.php
* @version 150625:6
* @author karminski <code.karminski@outlook.com>
*
* @changes
* 150625:6 add forward option.
* 150608:5 remove singleCurl() method.
* 140809:4 FIX urlencode option value.
* 140719:3 ADD urlencode option.
* 140719:2 ADD httpRequest() method.
* 130927:1 ADD singleCurl(), httpRequestPost() method.
*/
namespace SaltFish;
/**
* CLASS Curl
*/
class Curl{
/**
* [httpRequestPost description]
* @param [type] $args [description]
* @return [type] [description]
*/
public static function httpRequest($args){
// start
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $args['url']);
// set UA
if(!empty($args['useragent'])){
curl_setopt($ch, CURLOPT_USERAGENT, $args['useragent']);
}
// set time out
if(!empty($args['timeout'])){
curl_setopt($ch, CURLOPT_TIMEOUT, $args['timeout']);
}
// set data
if(!empty($args['data'])){
if(!empty($args['urlencode'])){
$urlencode = true;
}else{
$urlencode = false;
}
curl_setopt($ch, CURLOPT_POSTFIELDS, self::formData($args['data'], $urlencode));
}
// set header
if(!empty($args['host'])){
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Host: '.$args['host'],
));
}
if(!empty($args['header'])){
curl_setopt($ch, CURLOPT_HTTPHEADER, $args['header']);
}
//set Cookie
if(!empty($args['cookie'])){
curl_setopt($ch, CURLOPT_COOKIE, $args['cookie']);
}
// set proxy
if(!empty($args['proxy'])){
curl_setopt($ch, CURLOPT_PROXY, $args['proxy']);
}
// set gzip
if(!empty($args['gzip'])){
curl_setopt($ch, CURLOPT_ENCODING , "gzip");
}
if(!empty($args['forward'])){
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
}
$result = curl_exec($ch);
$info = curl_getinfo($ch);
// var_dump($info);
unset($ch);
return $result;
}
/**
* [formData description]
* @param [type] $data [description]
* @return [type] [description]
*/
private static function formData($data, $urlencode = false){
if($urlencode){
return http_build_query($data);
}
$str = "";
foreach($data as $k => $v){
$str .= "&{$k}={$v}";
}
return $str;
}
}

+ 28
- 0
src/protected/lib/network/Ip.php View File

@ -0,0 +1,28 @@
<?php
/**
* Ip.php
* @version 150608:1
* @author karminski <code.karminski@outlook.com>
*
* @changes
* 150608:1 Add getClientIp().
*/
/**
* class Ip
*/
class Ip{
/**
* [getClientIp description]
* @return [type] [description]
*/
public static function getClientIp(){
return array(
'HTTP_X_FORWARDED_FOR' => empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? array() : explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']),
'HTTP_CLIENT_IP' => empty($_SERVER["HTTP_CLIENT_IP"])? "" : $_SERVER["HTTP_CLIENT_IP"],
'REMOTE_ADDR' => empty($_SERVER["REMOTE_ADDR"])? "" : $_SERVER["REMOTE_ADDR"],
);
}
}

+ 230
- 0
src/protected/lib/network/MultiCurl.php View File

@ -0,0 +1,230 @@
<?php
/*if(!class_exists('MultiCurlManager'))
include 'MultiCurlManager.php';
if(!class_exists('MultiCurlSequence'))
include 'MultiCurlSequence.php';
if(!class_exists('MultiCurlException'))
include 'MultiCurlException.php';*/
/**
* MultiCurl multicurl http client
*
* @author Jaisen Mathai <jaisen@jmathai.com>
*/
class MultiCurl
{
const timeout = 3;
private static $inst = null;
/* @TODO make this private and add a method to set it to 0 */
public static $singleton = 0;
private $mc;
private $running;
private $execStatus;
private $sleepIncrement = 1.1;
private $requests = array();
private $responses = array();
private $properties = array();
private static $timers = array();
public function __construct()
{
if(self::$singleton === 0)
{
throw new MultiCurlException('This class cannot be instantiated by the new keyword. You must instantiate it using: $obj = MultiCurl::getInstance();');
}
$this->mc = curl_multi_init();
$this->properties = array(
'code' => CURLINFO_HTTP_CODE,
'time' => CURLINFO_TOTAL_TIME,
'length'=> CURLINFO_CONTENT_LENGTH_DOWNLOAD,
'type' => CURLINFO_CONTENT_TYPE,
'url' => CURLINFO_EFFECTIVE_URL
);
}
public function addUrl($url, $options = array())
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
foreach($options as $option=>$value)
{
curl_setopt($ch, $option, $value);
}
return $this->addCurl($ch);
}
public function addCurl($ch)
{
if(gettype($ch) !== 'resource')
{
throw new MultiCurlInvalidParameterException('Parameter must be a valid curl handle');
}
$key = $this->getKey($ch);
$this->requests[$key] = $ch;
// uncomment this when you need header info
// curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'headerCallback'));
$code = curl_multi_add_handle($this->mc, $ch);
// uncomment this when you need timer;
// $this->startTimer($key);
// (1)
if($code === CURLM_OK || $code === CURLM_CALL_MULTI_PERFORM)
{
do
{
$this->execStatus = curl_multi_exec($this->mc, $this->running);
} while ($this->execStatus === CURLM_CALL_MULTI_PERFORM);
return new MultiCurlManager($key);
}
else
{
return $code;
}
}
public function getResult($key = null)
{
if($key != null)
{
if(isset($this->responses[$key]['code']))
{
return $this->responses[$key];
}
$innerSleepInt = $outerSleepInt = 1;
while($this->running && ($this->execStatus == CURLM_OK || $this->execStatus == CURLM_CALL_MULTI_PERFORM))
{
usleep(intval($outerSleepInt));
$outerSleepInt = intval(max(1, ($outerSleepInt*$this->sleepIncrement)));
$ms=curl_multi_select($this->mc, 0);
// bug in PHP 5.3.18+ where curl_multi_select can return -1
// https://bugs.php.net/bug.php?id=63411
if($ms === -1)
usleep(100000);
// see pull request https://github.com/jmathai/php-multi-curl/pull/17
// details here http://curl.haxx.se/libcurl/c/libcurl-errors.html
if($ms >= CURLM_CALL_MULTI_PERFORM)
{
do{
$this->execStatus = curl_multi_exec($this->mc, $this->running);
usleep(intval($innerSleepInt));
$innerSleepInt = intval(max(1, ($innerSleepInt*$this->sleepIncrement)));
}while($this->execStatus==CURLM_CALL_MULTI_PERFORM);
$innerSleepInt = 1;
}
$this->storeResponses();
if(isset($this->responses[$key]['data']))
{
return $this->responses[$key];
}
}
return null;
}
return false;
}
public static function getSequence()
{
return new MultiCurlSequence(self::$timers);
}
public static function getTimers()
{
return self::$timers;
}
public function inject($key, $value)
{
$this->$key = $value;
}
private function getKey($ch)
{
return (string)$ch;
}
private function headerCallback($ch, $header)
{
$_header = trim($header);
$colonPos= strpos($_header, ':');
if($colonPos > 0)
{
$key = substr($_header, 0, $colonPos);
$val = preg_replace('/^\W+/','',substr($_header, $colonPos));
$this->responses[$this->getKey($ch)]['headers'][$key] = $val;
}
return strlen($header);
}
private function storeResponses()
{
while($done = curl_multi_info_read($this->mc))
{
$this->storeResponse($done);
}
}
private function storeResponse($done, $isAsynchronous = true)
{
$key = $this->getKey($done['handle']);
// uncomment this line when you need timer.
// $this->stopTimer($key, $done);
if($isAsynchronous)
$this->responses[$key]['data'] = curl_multi_getcontent($done['handle']);
else
$this->responses[$key]['data'] = curl_exec($done['handle']);
$this->responses[$key]['response'] = $this->responses[$key]['data'];
foreach($this->properties as $name => $const)
{
$this->responses[$key][$name] = curl_getinfo($done['handle'], $const);
}
if($isAsynchronous)
curl_multi_remove_handle($this->mc, $done['handle']);
curl_close($done['handle']);
}
private function startTimer($key)
{
self::$timers[$key]['start'] = microtime(true);
}
private function stopTimer($key, $done)
{
self::$timers[$key]['end'] = microtime(true);
self::$timers[$key]['api'] = curl_getinfo($done['handle'], CURLINFO_EFFECTIVE_URL);
self::$timers[$key]['time'] = curl_getinfo($done['handle'], CURLINFO_TOTAL_TIME);
self::$timers[$key]['code'] = curl_getinfo($done['handle'], CURLINFO_HTTP_CODE);
}
public static function getInstance($new_instance = false)
{
if($new_instance){
self::$inst == null;
self::$singleton = 1;
self::$inst = new MultiCurl();
}
if(self::$inst == null){
self::$singleton = 1;
self::$inst = new MultiCurl();
}
return self::$inst;
}
}
/*
* Credits:
* - (1) Alistair pointed out that curl_multi_add_handle can return CURLM_CALL_MULTI_PERFORM on success.
*/

+ 3
- 0
src/protected/lib/network/MultiCurlException.php View File

@ -0,0 +1,3 @@
<?php
class MultiCurlException extends \Exception {}
class MultiCurlInvalidParameterException extends MultiCurlException {}

+ 29
- 0
src/protected/lib/network/MultiCurlManager.php View File

@ -0,0 +1,29 @@
<?php
/**
* MultiCurlManager manages multicurl handles
*
* @author Jaisen Mathai <jaisen@jmathai.com>
*/
class MultiCurlManager
{
private $key;
private $epiCurl;
public function __construct($key)
{
$this->key = $key;
$this->epiCurl = MultiCurl::getInstance();
}
public function __get($name)
{
$responses = $this->epiCurl->getResult($this->key);
return isset($responses[$name]) ? $responses[$name] : null;
}
public function __isset($name)
{
$val = self::__get($name);
return empty($val);
}
}

+ 71
- 0
src/protected/lib/network/MultiCurlSequence.php View File

@ -0,0 +1,71 @@
<?php
/**
* MultiCurlSequence displays sequence of http calls
*
* @author Jaisen Mathai <jaisen@jmathai.com>
*/
class MultiCurlSequence
{
private $width = 100;
private $timers;
private $min;
private $max;
private $range;
private $step;
public function __construct($timers)
{
$this->timers = $timers;
$min = PHP_INT_MAX;
$max = 0;
foreach($this->timers as $timer)
{
if(!isset($timer['start']))
$timer['start'] = PHP_INT_MAX;
if(!isset($timer['end']))
$timer['end'] = 0;
$min = min($timer['start'], $min);
$max = max($timer['end'], $max);
}
$this->min = $min;
$this->max = $max;
$this->range = $max-$min;
$this->step = floatval($this->range/$this->width);
}
public function renderAscii()
{
$tpl = '';
foreach($this->timers as $timer)
$tpl .= $this->tplAscii($timer);
return $tpl;
}
private function tplAscii($timer)
{
$lpad = $rpad = 0;
$lspace = $chars = $rspace = '';
if($timer['start'] > $this->min)
$lpad = intval(($timer['start'] - $this->min) / $this->step);
if($timer['end'] < $this->max)
$rpad = intval(($this->max - $timer['end']) / $this->step);
$mpad = $this->width - $lpad - $rpad;
if($lpad > 0)
$lspace = str_repeat(' ', $lpad);
if($mpad > 0)
$chars = str_repeat('=', $mpad);
if($rpad > 0)
$rspace = str_repeat(' ', $rpad);
$tpl = <<<TPL
({$timer['api']} :: code={$timer['code']}, start={$timer['start']}, end={$timer['end']}, total={$timer['time']})
[{$lspace}{$chars}{$rspace}]
TPL;
return $tpl;
}
}

+ 59
- 0
src/protected/lib/queue/AMQPFactory.php View File

@ -0,0 +1,59 @@
<?php
/**
* AMQPFactory.php
* AMQP connection factory.
* @version 160127:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 160127:1 INIT version.
*/
namespace SaltFish;
/**
* class AMQPFactory
*/
class AMQPFactory{
private static $instance;
public $h;
public $pool = array();
/**
* [__construct description]
* @param array &$h, redis all config
*/
private function __construct(){
$this->h = Config::get('queue');
return;
}
/**
* [getInstance description]
* @return [type] [description]
*/
public static function getInstance(){
if(empty(self::$instance)){
self::$instance = new AMQPFactory;
}
return self::$instance;
}
/**
* [spawn description]
* @param string $name, redis instance name
* @return RedisProxy RedisProxy
*/
public static function &spawn($name){
$instance = self::getInstance();
// if alerady spawn return pool result
if(!empty($instance->pool[$name])) return $instance->pool[$name];
// spawn
$instance->pool[$name] = new AMQPProxy($instance->h[$name]);
return $instance->pool[$name];
}
}

+ 106
- 0
src/protected/lib/queue/AMQPProxy.php View File

@ -0,0 +1,106 @@
<?php
/**
* AMQPProxy.php
* AMQP Proxy for rabbitMQ connection.
* @version 160127:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 160127:1 INIT version.
*
*/
namespace SaltFish;
/**
* CLASS AMQPProxy
*/
class AMQPProxy{
public $AMQPInstance;
public $connectInfo;
public $connection;
public $exchange;
public $channel;
public $queue;
/**
* [__construct description]
* @param array &$connectInfo.
* @return boolean connect result.
*/
public function __construct(&$connectInfo){
$this->connectInfo = $connectInfo;
return;
}
/**
* [__destruct description]
*/
public function __destruct(){
return $this->disconnect();
}
/**
* [consume description]
* @param [type] $callback_func [description]
* @return [type] [description]
*/
public function consume($callback_func){
$this->connect();
$this->queue->consume($callback_func);
}
/**
* [getName description]
* @return string name
*/
public function getName(){
return $this->connectInfo['name'];
}
/**
* [connect description]
* @return boolean success
*/
public function connect(){
if(empty($this->connection)){
// connect
$info = &$this->connectInfo;
$this->connection = new \AMQPConnection();
$this->connection->setHost($info['host']);
$this->connection->setLogin($info['user']);
$this->connection->setPassword($info['password']);
$this->connection->connect();
// set channel
$this->channel = new \AMQPChannel($this->connection);
// set exchange
$this->exchange = new \AMQPExchange($this->channel);
// spawn queue
$this->queue = new \AMQPQueue($this->channel);
$this->queue->setName($info['routing_key']);
$this->queue->setFlags($info['flags']);
$this->queue->declareQueue();
if(empty($this->queue)) return false;
}
return true;
}
/**
* [disconnect description]
* @return boolean result
*/
public function disconnect(){
if(!is_object($this->queue)) return false;
$this->queue->cancel();
if(!is_object($this->connection)) return false;
$connection->disconnect();
return true;
}
}

+ 291
- 0
src/protected/lib/storage/BaseQuery.php View File

@ -0,0 +1,291 @@
<?php
/** Base query builder
*/
abstract class BaseQuery implements IteratorAggregate {
/** @var FluentPDO */
private $fpdo;
/** @var array of definition clauses */
protected $clauses = array();
/** @var PDOStatement */
private $result;
/** @var float */
private $time;
/** @var bool */
private $object = false;
protected $statements = array(), $parameters = array();
protected function __construct(FluentPDO $fpdo, $clauses) {
$this->fpdo = $fpdo;
$this->clauses = $clauses;
$this->initClauses();
}
private function initClauses() {
foreach ($this->clauses as $clause => $value) {
if ($value) {
$this->statements[$clause] = array();
$this->parameters[$clause] = array();
} else {
$this->statements[$clause] = null;
$this->parameters[$clause] = null;
}
}
}
/**
* Add statement for all kind of clauses
* @param $clause
* @param $statement
* @param array $parameters
* @return $this|SelectQuery
*/
protected function addStatement($clause, $statement, $parameters = array()) {
if ($statement === null) {
return $this->resetClause($clause);
}
# $statement !== null
if ($this->clauses[$clause]) {
if (is_array($statement)) {
$this->statements[$clause] = array_merge($this->statements[$clause], $statement);
} else {
$this->statements[$clause][] = $statement;
}
$this->parameters[$clause] = array_merge($this->parameters[$clause], $parameters);
} else {
$this->statements[$clause] = $statement;
$this->parameters[$clause] = $parameters;
}
return $this;
}
/**
* Remove all prev defined statements
* @param $clause
* @return $this
*/
protected function resetClause($clause) {
$this->statements[$clause] = null;
$this->parameters[$clause] = array();
if ($this->clauses[$clause]) {
$this->statements[$clause] = array();
}
return $this;
}
/** Implements method from IteratorAggregate
* @return PDOStatement
*/
public function getIterator() {
return $this->execute();
}
/** Execute query with earlier added parameters
* @return PDOStatement
*/
public function execute() {
$query = $this->buildQuery();
$parameters = $this->buildParameters();
$result = $this->fpdo->getPdo()->prepare($query);
// At this point, $result is a PDOStatement instance, or false.
// PDO::prepare() does not reliably return errors. Some database drivers
// do not support prepared statements, and PHP emulates them. Postgres
// does support prepared statements, but PHP does not call Postgres's
// prepare function until we call PDOStatement::execute() (below).
// If PDO::prepare() worked properly, this is where we would check
// for prepare errors, such as invalid SQL.
if ($this->object !== false) {
if (class_exists($this->object)) {
$result->setFetchMode(PDO::FETCH_CLASS, $this->object);
} else {
$result->setFetchMode(PDO::FETCH_OBJ);
}
} elseif ($this->fpdo->getPdo()->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE) == PDO::FETCH_BOTH) {
$result->setFetchMode(PDO::FETCH_ASSOC);
}
$time = microtime(true);
if ($result && $result->execute($parameters)) {
$this->time = microtime(true) - $time;
} else {
$result = false;
}
$this->result = $result;
$this->debugger();
return $result;
}
private function debugger() {
if ($this->fpdo->debug) {
if (!is_callable($this->fpdo->debug)) {
$backtrace = '';
$query = $this->getQuery();
$parameters = $this->getParameters();
$debug = '';
if ($parameters) {
$debug = "# parameters: " . implode(", ", array_map(array($this, 'quote'), $parameters)) . "\n";
}
$debug .= $query;
$pattern = '(^' . preg_quote(dirname(__FILE__)) . '(\\.php$|[/\\\\]))'; // can be static
foreach (debug_backtrace() as $backtrace) {
if (isset($backtrace["file"]) && !preg_match($pattern, $backtrace["file"])) {
// stop on first file outside FluentPDO source codes
break;
}
}
$time = sprintf('%0.3f', $this->time * 1000) . ' ms';
$rows = ($this->result) ? $this->result->rowCount() : 0;
fwrite(STDERR, "# $backtrace[file]:$backtrace[line] ($time; rows = $rows)\n$debug\n\n");
} else {
call_user_func($this->fpdo->debug, $this);
}
}
}
/**
* @return \PDO
*/
protected function getPDO() {
return $this->fpdo->getPdo();
}
/**
* @return \FluentStructure
*/
protected function getStructure() {
return $this->fpdo->getStructure();
}
/** Get PDOStatement result
* @return \PDOStatement
*/
public function getResult() {
return $this->result;
}
/** Get time of execution
* @return float
*/
public function getTime() {
return $this->time;
}
/** Get query parameters
* @return array
*/
public function getParameters() {
return $this->buildParameters();
}
/** Get query string
* @param boolean $formated return formated query
* @return string
*/
public function getQuery($formated = true) {
$query = $this->buildQuery();
if ($formated) $query = FluentUtils::formatQuery($query);
return $query;
}
/**
* Generate query
* @return string
* @throws Exception
*/
protected function buildQuery() {
$query = '';
foreach ($this->clauses as $clause => $separator) {
if ($this->clauseNotEmpty($clause)) {
if (is_string($separator)) {
$query .= " $clause " . implode($separator, $this->statements[$clause]);
} elseif ($separator === null) {
$query .= " $clause " . $this->statements[$clause];
} elseif (is_callable($separator)) {
$query .= call_user_func($separator);
} else {
throw new Exception("Clause '$clause' is incorrectly set to '$separator'.");
}
}
}
return trim($query);
}
private function clauseNotEmpty($clause) {
if ($this->clauses[$clause]) {
return (boolean) count($this->statements[$clause]);
} else {
return (boolean) $this->statements[$clause];
}
}
private function buildParameters() {
$parameters = array();
foreach ($this->parameters as $clauses) {
if (is_array($clauses)) {
foreach ($clauses as $value) {
if (is_array($value) && is_string(key($value)) && substr(key($value), 0, 1) == ':') {
// this is named params e.g. (':name' => 'Mark')
$parameters = array_merge($parameters, $value);
} else {
$parameters[] = $value;
}
}
} else {
if ($clauses) $parameters[] = $clauses;
}
}
return $parameters;
}
protected function quote($value) {
if (!isset($value)) {
return "NULL";
}
if (is_array($value)) { // (a, b) IN ((1, 2), (3, 4))
return "(" . implode(", ", array_map(array($this, 'quote'), $value)) . ")";
}
$value = $this->formatValue($value);
if (is_float($value)) {
return sprintf("%F", $value); // otherwise depends on setlocale()
}
if ($value === false) {
return "0";
}
if (is_int($value) || $value instanceof FluentLiteral) { // number or SQL code - for example "NOW()"
return (string) $value;
}
return $this->fpdo->getPdo()->quote($value);
}
private function formatValue($val) {
if ($val instanceof DateTime) {
return $val->format("Y-m-d H:i:s"); //! may be driver specific
}
return $val;
}
/**
* Select an item as object
* @param boolean|object $object If set to true, items are returned as stdClass, otherwise a class
* name can be passed and a new instance of this class is return.
* Can be set to false to return items as an associative array.
* @return BaseQuery
*/
public function asObject($object = true) {
$this->object = $object;
return $this;
}
}

+ 230
- 0
src/protected/lib/storage/CommonQuery.php View File

@ -0,0 +1,230 @@
<?php
/** CommonQuery add JOIN and WHERE clauses for (SELECT, UPDATE, DELETE)
*/
abstract class CommonQuery extends BaseQuery {
/** @var array of used tables (also include table from clause FROM) */
protected $joins = array();
/** @var boolean disable adding undefined joins to query? */
protected $isSmartJoinEnabled = true;
public function enableSmartJoin() {
$this->isSmartJoinEnabled = true;
return $this;
}
public function disableSmartJoin() {
$this->isSmartJoinEnabled = false;
return $this;
}
public function isSmartJoinEnabled() {
return $this->isSmartJoinEnabled;
}
/** Add where condition, more calls appends with AND
* @param string $condition possibly containing ? or :name (PDO syntax)
* @param mixed $parameters array or a scalar value
* @return SelectQuery
*/
public function where($condition, $parameters = array()) {
if ($condition === null) {
return $this->resetClause('WHERE');
}
if (!$condition) {
return $this;
}
if (is_array($condition)) { // where(array("column1" => 1, "column2 > ?" => 2))
foreach ($condition as $key => $val) {
$this->where($key, $val);
}
return $this;
}
$args = func_get_args();
if (count($args) == 1) {
return $this->addStatement('WHERE', $condition);
}
if (count($args) == 2 && preg_match('~^[a-z_:][a-z0-9_.:]*$~i', $condition)) {
# condition is column only
if (is_null($parameters)) {
return $this->addStatement('WHERE', "$condition is NULL");
} elseif (is_array($args[1])) {
$in = $this->quote($args[1]);
return $this->addStatement('WHERE', "$condition IN $in");
}
$condition = "$condition = ?";
}
array_shift($args);
return $this->addStatement('WHERE', $condition, $args);
}
/**
* @param $clause
* @param array $parameters - first is $statement followed by $parameters
* @return $this|SelectQuery
*/
public function __call($clause, $parameters = array()) {
$clause = FluentUtils::toUpperWords($clause);
if ($clause == 'GROUP') $clause = 'GROUP BY';
if ($clause == 'ORDER') $clause = 'ORDER BY';
if ($clause == 'FOOT NOTE') $clause = "\n--";
$statement = array_shift($parameters);
if (strpos($clause, 'JOIN') !== FALSE) {
return $this->addJoinStatements($clause, $statement, $parameters);
}
return $this->addStatement($clause, $statement, $parameters);
}
protected function getClauseJoin() {
return implode(' ', $this->statements['JOIN']);
}
/**
* Statement can contain more tables (e.g. "table1.table2:table3:")
* @param $clause
* @param $statement
* @param array $parameters
* @return $this|SelectQuery
*/
private function addJoinStatements($clause, $statement, $parameters = array()) {
if ($statement === null) {
$this->joins = array();
return $this->resetClause('JOIN');
}
if (array_search(substr($statement, 0, -1), $this->joins) !== FALSE) {
return $this;
}
# match "tables AS alias"
preg_match('~`?([a-z_][a-z0-9_\.:]*)`?(\s+AS)?(\s+`?([a-z_][a-z0-9_]*)`?)?~i', $statement, $matches);
$joinAlias = '';
$joinTable = '';
if ($matches) {
$joinTable = $matches[1];
if (isset($matches[4]) && !in_array(strtoupper($matches[4]), array('ON', 'USING'))) {
$joinAlias = $matches[4];
}
}
if (strpos(strtoupper($statement), ' ON ') || strpos(strtoupper($statement), ' USING')) {
if (!$joinAlias) $joinAlias = $joinTable;
if (in_array($joinAlias, $this->joins)) {
return $this;
} else {
$this->joins[] = $joinAlias;
$statement = " $clause $statement";
return $this->addStatement('JOIN', $statement, $parameters);
}
}
# $joinTable is list of tables for join e.g.: table1.table2:table3....
if (!in_array(substr($joinTable, -1), array('.', ':'))) {
$joinTable .= '.';
}
preg_match_all('~([a-z_][a-z0-9_]*[\.:]?)~i', $joinTable, $matches);
if (isset($this->statements['FROM'])) {
$mainTable = $this->statements['FROM'];
} elseif (isset($this->statements['UPDATE'])) {
$mainTable = $this->statements['UPDATE'];
}
$lastItem = array_pop($matches[1]);
array_push($matches[1], $lastItem);
foreach ($matches[1] as $joinItem) {
if ($mainTable == substr($joinItem, 0, -1)) continue;
# use $joinAlias only for $lastItem
$alias = '';
if ($joinItem == $lastItem) $alias = $joinAlias;
$newJoin = $this->createJoinStatement($clause, $mainTable, $joinItem, $alias);
if ($newJoin) $this->addStatement('JOIN', $newJoin, $parameters);
$mainTable = $joinItem;
}
return $this;
}
/**
* Create join string
* @param $clause
* @param $mainTable
* @param $joinTable
* @param string $joinAlias
* @return string
*/
private function createJoinStatement($clause, $mainTable, $joinTable, $joinAlias = '') {
if (in_array(substr($mainTable, -1), array(':', '.'))) {
$mainTable = substr($mainTable, 0, -1);
}
$referenceDirection = substr($joinTable, -1);
$joinTable = substr($joinTable, 0, -1);
$asJoinAlias = '';
if ($joinAlias) {
$asJoinAlias = " AS $joinAlias";
} else {
$joinAlias = $joinTable;
}
if (in_array($joinAlias, $this->joins)) {
# if join exists don't create same again
return '';
} else {
$this->joins[] = $joinAlias;
}
if ($referenceDirection == ':') {
# back reference
$primaryKey = $this->getStructure()->getPrimaryKey($mainTable);
$foreignKey = $this->getStructure()->getForeignKey($mainTable);
return " $clause $joinTable$asJoinAlias ON $joinAlias.$foreignKey = $mainTable.$primaryKey";
} else {
$primaryKey = $this->getStructure()->getPrimaryKey($joinTable);
$foreignKey = $this->getStructure()->getForeignKey($joinTable);
return " $clause $joinTable$asJoinAlias ON $joinAlias.$primaryKey = $mainTable.$foreignKey";
}
}
/**
* @return string
*/
protected function buildQuery() {
# first create extra join from statements with columns with referenced tables
$statementsWithReferences = array('WHERE', 'SELECT', 'GROUP BY', 'ORDER BY');
foreach ($statementsWithReferences as $clause) {
if (array_key_exists($clause, $this->statements)) {
$this->statements[$clause] = array_map(array($this, 'createUndefinedJoins'), $this->statements[$clause]);
}
}
return parent::buildQuery();
}
/** Create undefined joins from statement with column with referenced tables
* @param string $statement
* @return string rewrited $statement (e.g. tab1.tab2:col => tab2.col)
*/
private function createUndefinedJoins($statement) {
if (!$this->isSmartJoinEnabled) {
return $statement;
}
preg_match_all('~\\b([a-z_][a-z0-9_.:]*[.:])[a-z_]*~i', $statement, $matches);
foreach ($matches[1] as $join) {
if (!in_array(substr($join, 0, -1), $this->joins)) {
$this->addJoinStatements('LEFT JOIN', $join);
}
}
# don't rewrite table from other databases
foreach ($this->joins as $join) {
if (strpos($join, '.') !== FALSE && strpos($statement, $join) === 0) {
return $statement;
}
}
# remove extra referenced tables (rewrite tab1.tab2:col => tab2.col)
$statement = preg_replace('~(?:\\b[a-z_][a-z0-9_.:]*[.:])?([a-z_][a-z0-9_]*)[.:]([a-z_*])~i', '\\1.\\2', $statement);
return $statement;
}
}

+ 72
- 0
src/protected/lib/storage/DeleteQuery.php View File

@ -0,0 +1,72 @@
<?php
/** DELETE query builder
*
* @method DeleteQuery leftJoin(string $statement) add LEFT JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method DeleteQuery innerJoin(string $statement) add INNER JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method DeleteQuery from(string $table) add LIMIT to query
* @method DeleteQuery orderBy(string $column) add ORDER BY to query
* @method DeleteQuery limit(int $limit) add LIMIT to query
*/
class DeleteQuery extends CommonQuery {
private $ignore = false;
public function __construct(FluentPDO $fpdo, $table) {
$clauses = array(
'DELETE FROM' => array($this, 'getClauseDeleteFrom'),
'DELETE' => array($this, 'getClauseDelete'),
'FROM' => null,
'JOIN' => array($this, 'getClauseJoin'),
'WHERE' => ' AND ',
'ORDER BY' => ', ',
'LIMIT' => null,
);
parent::__construct($fpdo, $clauses);
$this->statements['DELETE FROM'] = $table;
$this->statements['DELETE'] = $table;
}
/** DELETE IGNORE - Delete operation fails silently
* @return \DeleteQuery
*/
public function ignore() {
$this->ignore = true;
return $this;
}
/**
* @return string
*/
protected function buildQuery() {
if ($this->statements['FROM']) {
unset($this->clauses['DELETE FROM']);
} else {
unset($this->clauses['DELETE']);
}
return parent::buildQuery();
}
/** Execute DELETE query
* @return boolean
*/
public function execute() {
$result = parent::execute();
if ($result) {
return $result->rowCount();
}
return false;
}
protected function getClauseDelete() {
return 'DELETE' . ($this->ignore ? " IGNORE" : '') . ' ' . $this->statements['DELETE'];
}
protected function getClauseDeleteFrom() {
return 'DELETE' . ($this->ignore ? " IGNORE" : '') . ' FROM ' . $this->statements['DELETE FROM'];
}
}

+ 303
- 0
src/protected/lib/storage/File.php View File

@ -0,0 +1,303 @@
<?php
/**
* File.class.php
* This class inlcude some file & folder operation method.
* WARING: This script can works on linux only.
* @version 151231:13
* @author karminski <code.karminski@outlook.com>
*
* @changelog
* 151231:13 ADD cat().
* 151207:12 ADD openFile(), closeFile(), readByLine().
* 151207:11 ADD flush(), humanFilesize().
* 140721:10 ADD trim to loadFile() method.
* 140721:9 FIX static method declare.
* 140719:7 ADD move(), loadFile() method.
* 131117:6 FIX txtEcho '>' method should be an atomic action
* 131012:5 FIX loadFolderFilename() read . or .. issue
* 130912:4 ADD txtReadByLineNum
* getLineNumByContent
* 130903:3 ADD txtTail
* 130902:2 ADD txtEcho
* txtHead
* auto_warp
* 130831:1 ADD loadFolderFilename
* txtWrite
*
*/
namespace SaltFish;
/**
* class File
*/
class File{
private static $instance;
public $fd;
public function __construct(){
}
public function __destruct(){
$this->closeFile();
}
/**
* [getInstance description]
* @return [type] [description]
*/
public static function getInstance(){
if(empty(self::$instance)){
self::$instance = new File;
}
return self::$instance;
}
/**
* PUBLIC STATIC FUNCTION loadFolderFilename
* This method load all filenames which in target folder into an array.
* @param string $dir, dir path
* @return array $files, array which loaded filenames
*/
public static function loadFolderFilename($dir){
if(empty($dir)) return false;
$files = array();
if($handle = opendir($dir)){
while(false !== ($file = readdir($handle))){
if($file === '.' || $file === '..') continue;
$files[] = $file;
}
closedir($handle);
}else{
return false;
}
return $files;
}
/**
* PUBLIC STATIC FUNCTION txtWrite
* This method use php BIF(build in function) 'error_log' to write files.
* @param string $str, what you want to write
* @param string $file, target file loaction
* @param string $auto_wrap, there's 'unix'. 'windows'('win'), 'mac', null(no-warp) offered.
* @return boolean status, write status.
*/
public static function txtWrite($str='', $file, $auto_wrap = ''){
if(empty($file)) return false;
self::auto_warp($str, $auto_wrap);
return error_log($str, 3, $file);
}
/**
* [txtEcho description]
* @param [type] $str [description]
* @param string $mode [description]
* @param [type] $file [description]
* @param string $auto_wrap [description]
* @return [type] [description]
*/
public static function txtEcho($str, $mode ='>', $file, $auto_wrap = ''){
if(empty($file)) return false;
self::auto_warp($str, $auto_wrap);
$com = "echo '{$str}' ";
if($mode === '>'){
$com .= ' > ';
$fileTmp = $file.".tmp";
$com .= $fileTmp;
if(exec($com) !== "") return false;
return (exec("mv $fileTmp $file")===""?true:false);
}elseif($mode === '>>'){
$com .= ' >> ';
$com .= $file;
return (exec($com)===""?true:false);
}
return false;
}
/**
* [txtHead description]
* @param [type] $file [description]
* @param integer $lines [description]
* @param string $bytes [description]
* @return [type] [description]
*/
public static function txtHead($file, $lines = 1, $bytes = ''){
if(empty($file)) return false;
$com = "head";
if(!empty($lines)) $com .= " -n {$lines} ";
if(!empty($bytes)) $com .= " -c {$bytes} ";
$com .= $file;
return exec($com);
}
/**
* [txtTail description]
* @param [type] $file [description]
* @param integer $lines [description]
* @param string $bytes [description]
* @return [type] [description]
*/
public static function txtTail($file, $lines = 1, $bytes = ''){
if(empty($file)) return false;
$com = "tail";
if(!empty($lines)) $com .= " -n {$lines} ";
if(!empty($bytes)) $com .= " -c {$bytes} ";
$com .= $file;
return exec($com);
}
/**
* [txtReadByLineNum description]
* @param [type] $line [description]
* @param [type] $file [description]
* @return [type] [description]
*/
public static function txtReadByLineNum($line, $file){
if(empty($line)||empty($file)) return false;
return exec("sed -n {$line}p {$file}");
}
/**
* [getLineNumByContent description]
* @param [type] $content [description]
* @param [type] $file [description]
* @return [type] [description]
*/
public static function getLineNumByContent($content, $file){
if(empty($content)||empty($file)) return false;
$lines = exec(" awk '/{$content}/ {print NR}' $file");
return explode("\n", $lines);
}
/**
* [move description]
* @param [type] $target [description]
* @param [type] $from [description]
* @param [type] $to [description]
* @return [type] [description]
*/
public static function move($target, $from, $to){
if(empty($from)||empty($to)) return false;
exec("mv {$from}{$target} {$to}",$out, $rc);
if($rc === 0) return true;
return false;
}
/**
* [flush description]
* @param [type] $target [description]
* @return [type] [description]
*/
public static function flush($target){
if(empty($target)) return false;
exec("echo '' > {$target}",$out, $rc);
if($rc === 0) return true;
return false;
}
/**
* [cat description]
* @param [type] $target [description]
* @param [type] $out [description]
* @return [type] [description]
*/
public static function cat($target, &$out){
if(empty($target)) return false;
exec("cat {$target}", $out, $rc);
if(empty($out)) return false;
return true;
}
/**
* [loadFile description]
* @param [type] $file_addr [description]
* @return [type] [description]
*/
public static function loadFile($file_addr, &$content, &$content_len){
$content = array();
$content_len = 0;
$requestDataHandle = @fopen($file_addr, "r");
if($requestDataHandle){
while(!feof($requestDataHandle)){
$content[] = trim(fgets($requestDataHandle));
$content_len ++;
}
fclose($requestDataHandle);
}else{
return false;
}
unset($requestDataHandle);
return true;
}
/**
* [openFile description]
* @param [type] $file [description]
* @return [type] [description]
*/
public function openFile($file){
$fd = @fopen($file, "r");
if(!$fd) return false;
$this->fd = $fd;
return true;
}
/**
* [closeFile description]
* @return [type] [description]
*/
public function closeFile(){
if(!empty($this->df)) fclose($this->fd);
}
/**
* [readByLine description]
* @param string $file [description]
* @param [type] &$line [description]
* @param integer $buffer_size [description]
* @return [type] [description]
*/
public function readByLine(&$line, $buffer_size = 4096){
if(!empty($this->fd) && !feof($this->fd)){
$line = fgets($this->fd, $buffer_size);
return true;
}else{
return false;
}
}
/**
* [humanFilesize description]
* @param [type] $bytes [description]
* @param integer $decimals [description]
* @return [type] [description]
*/
public static function humanFilesize($bytes, $decimals=2){
$sz = 'BKMGTP';
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
}
/**
* [auto_warp description]
* @param [type] $str [description]
* @param string $mode [description]
* @return [type] [description]
*/
private static function auto_warp(&$str, $mode =''){
if(!empty($mode)){
if($mode === 'unix'){
$str .= "\n";
}elseif($mode === 'win' || $mode === 'windows'){
$str .= "\r\n";
}elseif($mode === 'mac'){
$str .= "\r";
}
}
}
}

+ 23
- 0
src/protected/lib/storage/FluentLiteral.php View File

@ -0,0 +1,23 @@
<?php
/** SQL literal value
*/
class FluentLiteral {
protected $value = '';
/** Create literal value
* @param string $value
*/
function __construct($value) {
$this->value = $value;
}
/** Get literal value
* @return string
*/
function __toString() {
return $this->value;
}
}

+ 133
- 0
src/protected/lib/storage/FluentPDO.php View File

@ -0,0 +1,133 @@
<?php
/**
* FluentPDO is simple and smart SQL query builder for PDO
*
* For more information @see readme.md
*
* @link http://github.com/lichtner/fluentpdo
* @author Marek Lichtner, marek@licht.sk
* @copyright 2012 Marek Lichtner
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
*/
include_once 'FluentStructure.php';
include_once 'FluentUtils.php';
include_once 'FluentLiteral.php';
include_once 'BaseQuery.php';
include_once 'CommonQuery.php';
include_once 'SelectQuery.php';
include_once 'InsertQuery.php';
include_once 'ReplaceQuery.php';
include_once 'UpdateQuery.php';
include_once 'DeleteQuery.php';
class FluentPDO {
private $pdo, $structure;
/** @var boolean|callback */
public $debug;
function __construct(PDO $pdo, FluentStructure $structure = null) {
$this->pdo = $pdo;
if (!$structure) {
$structure = new FluentStructure;
}
$this->structure = $structure;
}
/** Create SELECT query from $table
* @param string $table db table name
* @param integer $primaryKey return one row by primary key
* @return \SelectQuery
*/
public function from($table, $primaryKey = null) {
$query = new SelectQuery($this, $table);
if ($primaryKey) {
$tableTable = $query->getFromTable();
$tableAlias = $query->getFromAlias();
$primaryKeyName = $this->structure->getPrimaryKey($tableTable);
$query = $query->where("$tableAlias.$primaryKeyName", $primaryKey);
}
return $query;
}
/** Create INSERT INTO query
*
* @param string $table
* @param array $values you can add one or multi rows array @see docs
* @return \InsertQuery
*/
public function insertInto($table, $values = array()) {
$query = new InsertQuery($this, $table, $values);
return $query;
}
/** Create REPLACE INTO query
*
* @param string $table
* @param array $values you can add one or multi rows array @see docs
* @return \InsertQuery
*/
public function replaceInto($table, $values = array()) {
$query = new ReplaceQuery($this, $table, $values);
return $query;
}
/** Create UPDATE query
*
* @param string $table
* @param array|string $set
* @param string $primaryKey
*
* @return \UpdateQuery
*/
public function update($table, $set = array(), $primaryKey = null) {
$query = new UpdateQuery($this, $table);
$query->set($set);
if ($primaryKey) {
$primaryKeyName = $this->getStructure()->getPrimaryKey($table);
$query = $query->where($primaryKeyName, $primaryKey);
}
return $query;
}
/** Create DELETE query
*
* @param string $table
* @param string $primaryKey delete only row by primary key
* @return \DeleteQuery
*/
public function delete($table, $primaryKey = null) {
$query = new DeleteQuery($this, $table);
if ($primaryKey) {
$primaryKeyName = $this->getStructure()->getPrimaryKey($table);
$query = $query->where($primaryKeyName, $primaryKey);
}
return $query;
}
/** Create DELETE FROM query
*
* @param string $table
* @param string $primaryKey
* @return \DeleteQuery
*/
public function deleteFrom($table, $primaryKey = null) {
$args = func_get_args();
return call_user_func_array(array($this, 'delete'), $args);
}
/** @return \PDO
*/
public function getPdo() {
return $this->pdo;
}
/** @return \FluentStructure
*/
public function getStructure() {
return $this->structure;
}
}

+ 29
- 0
src/protected/lib/storage/FluentStructure.php View File

@ -0,0 +1,29 @@
<?php
class FluentStructure {
private $primaryKey, $foreignKey;
function __construct($primaryKey = 'id', $foreignKey = '%s_id') {
if ($foreignKey === null) {
$foreignKey = $primaryKey;
}
$this->primaryKey = $primaryKey;
$this->foreignKey = $foreignKey;
}
public function getPrimaryKey($table) {
return $this->key($this->primaryKey, $table);
}
public function getForeignKey($table) {
return $this->key($this->foreignKey, $table);
}
private function key($key, $table) {
if (is_callable($key)) {
return $key($table);
}
return sprintf($key, $table);
}
}

+ 26
- 0
src/protected/lib/storage/FluentUtils.php View File

@ -0,0 +1,26 @@
<?php
class FluentUtils {
/** Convert "camelCaseWord" to "CAMEL CASE WORD"
* @param string $string
* @return string
*/
public static function toUpperWords($string) {
return trim(strtoupper(preg_replace('#(.)([A-Z]+)#', '$1 $2', $string)));
}
public static function formatQuery($query) {
$query = preg_replace(
'/WHERE|FROM|GROUP BY|HAVING|ORDER BY|LIMIT|OFFSET|UNION|ON DUPLICATE KEY UPDATE|VALUES/',
"\n$0", $query
);
$query = preg_replace(
'/INNER|LEFT|RIGHT|CASE|WHEN|END|ELSE|AND/',
"\n $0", $query
);
# remove trailing spaces
$query = preg_replace("/\s+\n/", "\n", $query);
return $query;
}
}

+ 124
- 0
src/protected/lib/storage/InsertQuery.php View File

@ -0,0 +1,124 @@
<?php
/** INSERT query builder
*/
class InsertQuery extends BaseQuery {
private $columns = array();
private $firstValue = array();
private $ignore = false;
public function __construct(FluentPDO $fpdo, $table, $values) {
$clauses = array(
'INSERT INTO' => array($this, 'getClauseInsertInto'),
'VALUES' => array($this, 'getClauseValues'),
'ON DUPLICATE KEY UPDATE' => array($this, 'getClauseOnDuplicateKeyUpdate'),
);
parent::__construct($fpdo, $clauses);
$this->statements['INSERT INTO'] = $table;
$this->values($values);
}
/** Execute insert query
* @return integer last inserted id or false
*/
public function execute() {
$result = parent::execute();
if ($result) {
return $this->getPDO()->lastInsertId();
}
return false;
}
/** Add ON DUPLICATE KEY UPDATE
* @param array $values
* @return \InsertQuery
*/
public function onDuplicateKeyUpdate($values) {
$this->statements['ON DUPLICATE KEY UPDATE'] = array_merge(
$this->statements['ON DUPLICATE KEY UPDATE'], $values
);
return $this;
}
/**
* Add VALUES
* @param $values
* @return \InsertQuery
* @throws Exception
*/
public function values($values) {
if (!is_array($values)) {
throw new Exception('Param VALUES for INSERT query must be array');
}
$first = current($values);
if (is_string(key($values))) {
# is one row array
$this->addOneValue($values);
} elseif (is_array($first) && is_string(key($first))) {
# this is multi values
foreach ($values as $oneValue) {
$this->addOneValue($oneValue);
}
}
return $this;
}
/** INSERT IGNORE - insert operation fails silently
* @return \InsertQuery
*/
public function ignore() {
$this->ignore = true;
return $this;
}
protected function getClauseInsertInto() {
return 'INSERT' . ($this->ignore ? " IGNORE" : '') . ' INTO ' . $this->statements['INSERT INTO'];
}
protected function getClauseValues() {
$valuesArray = array();
foreach ($this->statements['VALUES'] as $rows) {
$quoted = array_map(array($this, 'quote'), $rows);
$valuesArray[] = '(' . implode(', ', $quoted) . ')';
}
$columns = "`".implode('`, `', $this->columns)."`";
$values = implode(', ', $valuesArray);
return " ($columns) VALUES $values";
}
protected function getClauseOnDuplicateKeyUpdate() {
$result = array();
foreach ($this->statements['ON DUPLICATE KEY UPDATE'] as $key => $value) {
$result[] = "$key = " . $this->quote($value);
}
return ' ON DUPLICATE KEY UPDATE ' . implode(', ', $result);
}
private function addOneValue($oneValue) {
# check if all $keys are strings
foreach ($oneValue as $key => $value) {
if (!is_string($key)) {
throw new Exception('INSERT query: All keys of value array have to be strings.');
}
}
if (!$this->firstValue) {
$this->firstValue = $oneValue;
}
if (!$this->columns) {
$this->columns = array_keys($oneValue);
}
if ($this->columns != array_keys($oneValue)) {
throw new Exception('INSERT query: All VALUES have to same keys (columns).');
}
$this->statements['VALUES'][] = $oneValue;
}
}

+ 57
- 0
src/protected/lib/storage/MongoDBFactory.php View File

@ -0,0 +1,57 @@
<?php
/**
* MongoDBFactory.php
* MongoDB connection factory.
* @version 160106:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 160106:1 INIT version.
*/
namespace SaltFish;
/**
* class MongoDBFactory
*/
class MongoDBFactory{
private static $instance;
public $h;
public $pool = array();
/**
* [__construct description]
*/
private function __construct(){
$this->h = Config::get('mongodb');
return;
}
/**
* [getInstance description]
* @return [type] [description]
*/
public static function getInstance(){
if(empty(self::$instance)){
self::$instance = new MongoDBFactory;
}
return self::$instance;
}
/**
* [spawn description]
* @param string $name, mysql instance name
* @return MysqlProxy MysqlProxy
*/
public static function &spawn($name){
$instance = self::getInstance();
// if alerady spawn return pool result
if(!empty($instance->pool[$name])) return $instance->pool[$name];
// spawn
$instance->pool[$name] = new \MongoDB\Driver\Manager($instance->h[$name]);
return $instance->pool[$name];
}
}

+ 57
- 0
src/protected/lib/storage/MysqlFactory.php View File

@ -0,0 +1,57 @@
<?php
/**
* MysqlFactory.php
* Mysql connection factory.
* @version 151110:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 151110:1 INIT version.
*/
namespace SaltFish;
/**
* class MysqlFactory
*/
class MysqlFactory{
private static $instance;
public $h;
public $pool = array();
/**
* [__construct description]
*/
private function __construct(){
$this->h = Config::get('storage');
return;
}
/**
* [getInstance description]
* @return [type] [description]
*/
public static function getInstance(){
if(empty(self::$instance)){
self::$instance = new MysqlFactory;
}
return self::$instance;
}
/**
* [spawn description]
* @param string $name, mysql instance name
* @return MysqlProxy MysqlProxy
*/
public static function &spawn($name){
$instance = self::getInstance();
// if alerady spawn return pool result
if(!empty($instance->pool[$name])) return $instance->pool[$name];
// spawn
$instance->pool[$name] = new MysqlProxy($instance->h[$name]);
return $instance->pool[$name];
}
}

+ 123
- 0
src/protected/lib/storage/MysqlProxy.php View File

@ -0,0 +1,123 @@
<?php
/**
* MysqlProxy.php
* Mysql Proxy for mysql connection.
* @version 151110:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 141109:1 INIT version.
*
*/
namespace SaltFish;
/**
* CLASS MysqlProxy
*/
class MysqlProxy{
public $MysqlInstance;
public $connectInfo;
public $DBH; // PDO DBH
/**
* [__construct description]
* @param array &$connectInfo, e.g. array(host=>'', port=>3306, pass=>'', database=>0).
* @return boolean connect result.
*/
public function __construct(&$connectInfo){
$this->connectInfo = $connectInfo;
return;
}
/**
* [__destruct description]
*/
public function __destruct(){
return $this->disconnect();
}
/**
* [__call description]
* @param string $name, called function name
* @param array $args, input args
* @return mixed function call return
*/
public function __call($name, $args){
// check connection
if(!$this->connect()) return false;
// call instance
$argnum = count($args);
if($argnum === 1){
return $this->MysqlInstance->$name($args[0]);
}elseif($argnum === 2){
return $this->MysqlInstance->$name($args[0], $args[1]);
}elseif($argnum === 3){
return $this->MysqlInstance->$name($args[0], $args[1], $args[2]);
}elseif($argnum === 4){
return $this->MysqlInstance->$name($args[0], $args[1], $args[2], $args[3]);
}
return false;
}
/**
* [getName description]
* @return string name
*/
public function getName(){
return $this->connectInfo['name'];
}
/**
* [connect description]
* @return boolean success
*/
public function connect(){
if(empty($this->DBH)){
$info = &$this->connectInfo;
$opt = array(
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC
);
echo "============1\n";
print_r($info);
$this->DBH = new \PDO(
"mysql:host={$info['host']};port={$info['port']};dbname={$info['name']};charset={$info['charset']}",
$info['user'],
$info['pass'],
$opt
);
if(empty($this->DBH)) return false;
$this->MysqlInstance = new \FluentPDO($this->DBH);
if(empty($this->MysqlInstance)) return false;
if(!empty($info['debug'])){
$this->MysqlInstance->debug = function(){
echo "query: " . $BaseQuery->getQuery(false) . "\n";
echo "parameters: " . implode(', ', $BaseQuery->getParameters()) . "\n";
echo "rowCount: " . $BaseQuery->getResult()->rowCount() . "\n";
// time is impossible to test (each time is other)
// echo $FluentQuery->getTime() . "\n";
};
}
}
return true;
}
/**
* [disconnect description]
* @return boolean result
*/
public function disconnect(){
if(!empty($this->DBH)){
$this->DBH = null;
}
return true;
}
}

+ 124
- 0
src/protected/lib/storage/ReplaceQuery.php View File

@ -0,0 +1,124 @@
<?php
/** REPLACE query builder
*/
class ReplaceQuery extends BaseQuery {
private $columns = array();
private $firstValue = array();
private $ignore = false;
public function __construct(FluentPDO $fpdo, $table, $values) {
$clauses = array(
'REPLACE INTO' => array($this, 'getClauseReplaceInto'),
'VALUES' => array($this, 'getClauseValues'),
'ON DUPLICATE KEY UPDATE' => array($this, 'getClauseOnDuplicateKeyUpdate'),
);
parent::__construct($fpdo, $clauses);
$this->statements['REPLACE INTO'] = $table;
$this->values($values);
}
/** Execute insert query
* @return integer last inserted id or false
*/
public function execute() {
$result = parent::execute();
if ($result) {
return $this->getPDO()->lastInsertId();
}
return false;
}
/** Add ON DUPLICATE KEY UPDATE
* @param array $values
* @return \ReplaceQuery
*/
public function onDuplicateKeyUpdate($values) {
$this->statements['ON DUPLICATE KEY UPDATE'] = array_merge(
$this->statements['ON DUPLICATE KEY UPDATE'], $values
);
return $this;
}
/**
* Add VALUES
* @param $values
* @return \ReplaceQuery
* @throws Exception
*/
public function values($values) {
if (!is_array($values)) {
throw new Exception('Param VALUES for REPLACE query must be array');
}
$first = current($values);
if (is_string(key($values))) {
# is one row array
$this->addOneValue($values);
} elseif (is_array($first) && is_string(key($first))) {
# this is multi values
foreach ($values as $oneValue) {
$this->addOneValue($oneValue);
}
}
return $this;
}
/** REPLACE IGNORE - insert operation fails silently
* @return \ReplaceQuery
*/
public function ignore() {
$this->ignore = true;
return $this;
}
protected function getClauseReplaceInto() {
return 'REPLACE' . ($this->ignore ? " IGNORE" : '') . ' INTO ' . $this->statements['REPLACE INTO'];
}
protected function getClauseValues() {
$valuesArray = array();
foreach ($this->statements['VALUES'] as $rows) {
$quoted = array_map(array($this, 'quote'), $rows);
$valuesArray[] = '(' . implode(', ', $quoted) . ')';
}
$columns = "`".implode('`, `', $this->columns)."`";
$values = implode(', ', $valuesArray);
return " ($columns) VALUES $values";
}
protected function getClauseOnDuplicateKeyUpdate() {
$result = array();
foreach ($this->statements['ON DUPLICATE KEY UPDATE'] as $key => $value) {
$result[] = "$key = " . $this->quote($value);
}
return ' ON DUPLICATE KEY UPDATE ' . implode(', ', $result);
}
private function addOneValue($oneValue) {
# check if all $keys are strings
foreach ($oneValue as $key => $value) {
if (!is_string($key)) {
throw new Exception('REPLACE query: All keys of value array have to be strings.');
}
}
if (!$this->firstValue) {
$this->firstValue = $oneValue;
}
if (!$this->columns) {
$this->columns = array_keys($oneValue);
}
if ($this->columns != array_keys($oneValue)) {
throw new Exception('REPLACE query: All VALUES have to same keys (columns).');
}
$this->statements['VALUES'][] = $oneValue;
}
}

+ 133
- 0
src/protected/lib/storage/SelectQuery.php View File

@ -0,0 +1,133 @@
<?php
/**
* SELECT query builder
*
* @method SelectQuery select(string $column) add one or more columns in SELECT to query
* @method SelectQuery leftJoin(string $statement) add LEFT JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method SelectQuery innerJoin(string $statement) add INNER JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method SelectQuery groupBy(string $column) add GROUP BY to query
* @method SelectQuery having(string $column) add HAVING query
* @method SelectQuery orderBy(string $column) add ORDER BY to query
* @method SelectQuery limit(int $limit) add LIMIT to query
* @method SelectQuery offset(int $offset) add OFFSET to query
*/
class SelectQuery extends CommonQuery {
private $fromTable, $fromAlias;
function __construct(FluentPDO $fpdo, $from) {
$clauses = array(
'SELECT' => ', ',
'FROM' => null,
'JOIN' => array($this, 'getClauseJoin'),
'WHERE' => ' AND ',
'GROUP BY' => ',',
'HAVING' => ' AND ',
'ORDER BY' => ', ',
'LIMIT' => null,
'OFFSET' => null,
"\n--" => "\n--",
);
parent::__construct($fpdo, $clauses);
# initialize statements
$fromParts = explode(' ', $from);
$this->fromTable = reset($fromParts);
$this->fromAlias = end($fromParts);
$this->statements['FROM'] = $from;
$this->statements['SELECT'][] = $this->fromAlias . '.*';
$this->joins[] = $this->fromAlias;
}
/** Return table name from FROM clause
* @internal
*/
public function getFromTable() {
return $this->fromTable;
}
/** Return table alias from FROM clause
* @internal
*/
public function getFromAlias() {
return $this->fromAlias;
}
/** Returns a single column
* @param int $columnNumber
* @return string
*/
public function fetchColumn($columnNumber = 0) {
if ($s = $this->execute()) {
return $s->fetchColumn($columnNumber);
}
return false;
}
/** Fetch first row or column
* @param string $column column name or empty string for the whole row
* @return mixed string, array or false if there is no row
*/
public function fetch($column = '') {
$return = $this->execute();
if ($return === false) {
return false;
}
$return = $return->fetch();
if ($return && $column != '') {
if (is_object($return)) {
return $return->{$column};
} else {
return $return[$column];
}
}
return $return;
}
/**
* Fetch pairs
* @param $key
* @param $value
* @param $object
* @return array of fetched rows as pairs
*/
public function fetchPairs($key, $value, $object = false) {
if ($s = $this->select(null)->select("$key, $value")->asObject($object)->execute()) {
return $s->fetchAll(PDO::FETCH_KEY_PAIR);
}
return false;
}
/** Fetch all row
* @param string $index specify index column
* @param string $selectOnly select columns which could be fetched
* @return array of fetched rows
*/
public function fetchAll($index = '', $selectOnly = '') {
if ($selectOnly) {
$this->select(null)->select($index . ', ' . $selectOnly);
}
if ($index) {
$data = array();
foreach ($this as $row) {
if (is_object($row)) {
$data[$row->{$index}] = $row;
} else {
$data[$row[$index]] = $row;
}
}
return $data;
} else {
if ($s = $this->execute()) {
return $s->fetchAll();
}
return false;
}
}
}

+ 91
- 0
src/protected/lib/storage/UpdateQuery.php View File

@ -0,0 +1,91 @@
<?php
/** UPDATE query builder
*
* @method UpdateQuery leftJoin(string $statement) add LEFT JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method UpdateQuery innerJoin(string $statement) add INNER JOIN to query
* ($statement can be 'table' name only or 'table:' means back reference)
* @method UpdateQuery orderBy(string $column) add ORDER BY to query
* @method UpdateQuery limit(int $limit) add LIMIT to query
*/
class UpdateQuery extends CommonQuery {
public function __construct(FluentPDO $fpdo, $table) {
$clauses = array(
'UPDATE' => array($this, 'getClauseUpdate'),
'JOIN' => array($this, 'getClauseJoin'),
'SET' => array($this, 'getClauseSet'),
'WHERE' => ' AND ',
'ORDER BY' => ', ',
'LIMIT' => null,
);
parent::__construct($fpdo, $clauses);
$this->statements['UPDATE'] = $table;
$tableParts = explode(' ', $table);
$this->joins[] = end($tableParts);
}
/**
* @param string|array $fieldOrArray
* @param null $value
* @return $this
* @throws Exception
*/
public function set($fieldOrArray, $value = false) {
if (!$fieldOrArray) {
return $this;
}
if (is_string($fieldOrArray) && $value !== false) {
$this->statements['SET'][$fieldOrArray] = $value;
} else {
if (!is_array($fieldOrArray)) {
throw new Exception('You must pass a value, or provide the SET list as an associative array. column => value');
} else {
foreach ($fieldOrArray as $field => $value) {
$this->statements['SET'][$field] = $value;
}
}
}
return $this;
}
/** Execute update query
* @param boolean $getResultAsPdoStatement true to return the pdo statement instead of row count
* @return int|boolean|PDOStatement
*/
public function execute($getResultAsPdoStatement = false) {
$result = parent::execute();
if ($getResultAsPdoStatement) {
return $result;
}
if ($result) {
return $result->rowCount();
}
return false;
}
protected function getClauseUpdate() {
return 'UPDATE ' . $this->statements['UPDATE'];
}
protected function getClauseSet() {
$setArray = array();
foreach ($this->statements['SET'] as $field => $value) {
if ($value instanceof FluentLiteral) {
$setArray[] = "`{$field}` = " . $value;
} else {
$setArray[] = "`{$field}` = ?";
$this->parameters['SET'][$field] = $value;
}
}
return ' SET ' . implode(', ', $setArray);
}
}

+ 52
- 0
src/protected/lib/string/String.php View File

@ -0,0 +1,52 @@
<?php
/**
* String.php
* utils of string.
* @version 150609:1
* @author karminski <code.karminski@outlook.com>
*
*/
/**
* class String
*/
class String{
/**
* [utf8Split description]
* @param [type] $utf8_str [description]
* @return [type] [description]
*/
public static function utf8Split(&$utf8_str){
$s = $utf8_str;
$len = strlen($s);
if($len == 0) return array();
$chars = array();
for($i = 0;$i < $len;$i++){
$c = $s[$i];
$n = ord($c);
if(($n >> 7) == 0){ // 0xxx xxxx, asci, single
$chars[] = $c;
}
else if(($n >> 4) == 15){ // 1111 xxxx, first in four char
if($i < $len - 3){
$chars[] = $c.$s[$i + 1].$s[$i + 2].$s[$i + 3];
$i += 3;
}
}
else if(($n >> 5) == 7){ // 111x xxxx, first in three char
if($i < $len - 2){
$chars[] = $c.$s[$i + 1].$s[$i + 2];
$i += 2;
}
}
else if(($n >> 6) == 3){ // 11xx xxxx, first in two char
if($i < $len - 1){
$chars[] = $c.$s[$i + 1];
$i++;
}
}
}
return $chars;
}
}

+ 32
- 0
src/protected/lib/system/Process.php View File

@ -0,0 +1,32 @@
<?php
/**
* Process.php
* This class inlcude some process operation method.
* WARING: This script can works on linux only.
* @author karminski <code.karminski@outlook.com>
* @version 131012
*
* @changelog
* 131012 init version.
*
*
*/
/**
* class Process
*/
class Process{
/**
* [getProcessNumbers description]
* @param [type] $process_name [description]
* @return [type] [description]
*/
public static function getProcessNumbers($process_name){
if(empty($process_name)) return 0;
$com = "ps aux | grep {$process_name} | wc -l";
return (int)exec($com)-1; // -1 for grep
}
}

+ 60
- 0
src/protected/lib/xml/Sxml.php View File

@ -0,0 +1,60 @@
<?php
/**
* Sxml.php
* utils of sxml.
* @version 150810:1
* @author karminski <code.karminski@outlook.com>
*
*/
/**
* class Sxml
*/
class Sxml{
const SXML_ATOM_TMPL = '<%s:%s>';
/**
* [encryptSxml description]
* @param [type] &$data [description]
* @param string &$retval [description]
* @return [type] [description]
*/
public static function encrypt(&$data, &$retval = ""){
foreach($data as $key => $value){
$retval .= sprintf(self::SXML_ATOM_TMPL, $key, $value);
}
return;
}
/**
* [decryptSxml description]
* @param [type] $line [description]
* @return [type] [description]
*/
public static function decrypt(&$line, &$retval = array()){
$stage1 = explode("><", $line);
$stage1Len = count($stage1);
// single field
if($stage1Len===1){
return false;
}
// multifield ok
foreach($stage1 as $serial => $subLine){
if($serial === 0){
$subLine = str_replace("<", "", $subLine);
}
if($serial === ($stage1Len-1)){
$subLine = str_replace(">", "", $subLine);
}
$stage2 = explode(":", $subLine);
$key = $stage2[0];
unset($stage2[0]);
$value = implode(":", $stage2); // avoide ":" in data
$retval[$key] = $value;
}
return true;
}
}

+ 39
- 0
src/protected/model/ApiProxy.php View File

@ -0,0 +1,39 @@
<?php
/**
* ApiProxy.php
* ApiProxy model.
* @version 161101:2
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 160905:1 INIT version.
*/
/**
* abstract class ApiProxy
*/
abstract class ApiProxy{
/**
* [generateToken description]
* @param [type] $data [description]
* @return [type] [description]
*/
public static function generateToken($data, $public_key){
return SaltFish\AccessibilityCheck::generateToken($data, $public_key);
}
/**
* [generateTokenWithSha256 description]
* @param [type] $data [description]
* @param [type] $public_key [description]
* @return [type] [description]
*/
public static function generateTokenWithSha256($data, $public_key){
return SaltFish\AccessibilityCheck::generateTokenWithSha256($data, $public_key);
}
}

+ 29
- 0
src/protected/model/FeedbackInfo.php View File

@ -0,0 +1,29 @@
<?php
/**
* FeedbackInfo.php
* FeedbackInfo model.
* @author karminski <code.karminski@outlook.com>
* @version 160819:1
*/
/**
* class FeedbackInfo
*/
class FeedbackInfo{
// signals
const S_UNKNOWN_ERROR = 0;
const S_OK = 1;
const S_NO_RESULT = 2;
const S_PERMISSION_DENIED = 3;
const S_WRONG_INPUT = 4;
const S_WRONG_EXEC_RESULT = 5;
// messages
const M_OK = 'ok';
const M_ILLEGAL_SESSION = 'illegal session';
const M_ACCESS_DENIED = 'access denied';
const M_NO_RESULT = 'no result';
const M_ILLEGAL_TOKEN = 'illegal token';
const M_CACHE_FAILED = 'cache failed';
const M_STORAGE_FAILED = 'storage failed';
}

+ 60
- 0
src/protected/model/Request.php View File

@ -0,0 +1,60 @@
<?php
/**
* Request.php
* Request base model.
* @version 160825:4
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 160825:4 ADD getSrc() method.
* 151104:3 CHANGE field token to verify
* 151027:2 REMOVE exportForAccessibilityCheck().
* 151020:1 INIT version.
*/
/**
* abstract class Request
*/
abstract class Request{
public $token;
public $src;
/**
* [__construct description]
* @param [type] &$input [description]
*/
public function __construct(&$input){
return $this->setRequest($input);
}
/**
* [setRequest description]
* @param [type] &$input [description]
*/
public abstract function setRequest(&$input);
/**
* [getVerify description]
* @return [type] [description]
*/
public function getToken(){
return $this->token;
}
/**
* [getSrc description]
* @return [type] [description]
*/
public function getSrc(){
if(empty($this->src)) return '';
return $this->src;
}
/**
* exportForAccessibilityCheck()
* @return [type]
*/
public abstract function exportForAccessibilityCheck(&$data);
}

+ 18
- 0
src/protected/model/Result.php View File

@ -0,0 +1,18 @@
<?php
/**
* Result.php
* Result base model.
* @version 151027:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 151027:1 INIT version.
*/
/**
* abstract class Result
*/
abstract class Result{
}

+ 50
- 0
src/protected/model/SampleApiProxy.php View File

@ -0,0 +1,50 @@
<?php
/**
* SampleApiProxy.php
* Sample api proxy.
* @version 161011:2
* @author karminski <code.karminski@outlook.com>
*/
/**
* class LeancloudRestApiProxy
*/
class SampleApiProxy extends ApiProxy{
const API_SRC = 'sample_repo';
const API_PUBLIC_KEY = 'UPbGdCjQyrYgX8tn34Zl';
const REQUEST_TIMEOUT = 5; // seconds
const API_HOST = 'sample-api.juejin.im';
public static $GET_SAMPLE_API = array(
ENV_PRODUCT => 'http://192.168.0.1:8000/v1/get?id=%s&token=%s&src=%s',
ENV_BETA => 'http://192.168.0.2:8000/v1/get?id=%s&token=%s&src=%s',
ENV_TEST => 'http://192.168.0.3:8000/v1/get?id=%s&token=%s&src=%s',
);
/**
* [get description]
* @param SampleRequest $SampleRequest [description]
* @param SampleResult $SampleResult [description]
* @return [type] [description]
*/
public static function get(SampleRequest $SampleRequest, SampleResult $SampleResult){
$requestData = array(
'ids' => array()
);
$SampleRequest->exportAllIds($requestData['ids']);
$requestData['token'] = self::generateToken($requestData, self::API_PUBLIC_KEY);
$requestData['src'] = self::API_SRC;
$query = array(
'url' => sprintf(self::$GET_SAMPLE_API[SERVER_ENV], implode("|", $requestData['entryIds']), $requestData['token'], $requestData['src']),
'host' => self::API_HOST,
'timeout' => self::REQUEST_TIMEOUT,
'urlencode' => false,
);
$result = SaltFish\Curl::httpRequest($query);
return $SampleResult->setByApiProxy($result);
}
}

+ 63
- 0
src/protected/model/SampleCache.php View File

@ -0,0 +1,63 @@
<?php
/**
* SampleCache.php
* Sample cache
* @version 170821:1
* @author karminski <code.karminski@outlook.com>
*/
/**
* class SampleCache
*/
class SampleCache{
const INSTANCE_NAME = 'sample_cache';
const KEY_NAME_PREFIX = 'sc:';
public $Redis;
/**
* [__construct description]
*/
public function __construct(){
$this->Redis = SaltFish\RedisFactory::spawn(self::INSTANCE_NAME);
}
/**
* [__destruct description]
*/
public function __destruct(){
$this->Redis = null;
}
/**
* [set description]
* @param SampleRequest $SampleRequest [description]
*/
public function set(SampleRequest $SampleRequest){
$requestKey = "";
$requestData = "";
$SampleRequest->exportForSampleCacheSet($requestKey, $requestData);
return $this->Redis->set(self::KEY_NAME_PREFIX.$requestKey, $requestData);
}
/**
* [get description]
* @param SampleRequest $SampleRequest [description]
* @param SampleResult $SampleResult [description]
* @return [type] [description]
*/
public function get(SampleRequest $SampleRequest, SampleResult $SampleResult){
$requestKey = "";
// $resultData = "";
$resultData = '{"name":"test"}';
// if(!$SampleRequest->exportForSampleCacheGet($requestKey)) return false;
// $resultData = $this->Redis->get(self::KEY_NAME_PREFIX.$requestKey);
if(!$SampleResult->importResultFromCache($resultData)) return false;
return true;
}
}

+ 89
- 0
src/protected/model/SampleMongoDBStorage.php View File

@ -0,0 +1,89 @@
<?php
/**
* SampleMongoDBStorage.php
* Sample MongoDB storage model.
* @version 170410:1
* @author karminski <code.karminski@outlook.com>
*/
/**
* class SampleMongoDBStorage
*/
class SampleMongoDBStorage{
const CONNECTION_INFO = 'SampleMongoDBStorage';
const STORAGE_ADDRESS = 'SampleMongoDBStorage.Sample';
public $MongoDB;
/**
* [__construct description]
* @param [type] $redis [description]
*/
public function __construct(){
$this->MongoDB = SaltFish\MongoDBFactory::spawn(self::CONNECTION_INFO);
}
/**
* [get description]
* @param SampleRequest $SampleRequest [description]
* @param SampleResult $SampleResult [description]
* @return [type] [description]
*/
public function get(SampleRequest $SampleRequest, SampleResult $SampleResult){
$req = array();
if(!$SampleRequest->exportForSampleMongoDBStorageGet($req)) return false;
if($SampleRequest->isAfterRequest()){
$filter = array(
"createdAt" => array(
'$gt' => $req['after'],
),
);
}else{
$filter = array(
"createdAt" => array(
'$lt' => $req['before'],
),
);
}
$options = array(
'sort' => array("createdAt" => 1),
'limit' => SampleRequest::PAGE_LIMIT,
);
$query = new MongoDB\Driver\Query($filter, $options);
try{
$r = $this->MongoDB->executeQuery(self::STORAGE_ADDRESS, $query);
}
catch(MongoDB\Driver\Exception\BulkWriteException $e){
return false;
}
if(!is_object($r)) return false;
foreach($r as $doc){
if(empty($doc)) continue;
if(!$SampleResult->importFromSampleMongoDBStorageGet($doc)) return false;
}
return true;
}
/**
* [set description]
* @param SampleRequest $SampleRequest [description]
*/
public function set(SampleRequest $SampleRequest){
$data = array();
if(!$EntryStorageRequest->exportForSampleMongoDBStorageSet($data)) return false;
$bulk = new MongoDB\Driver\BulkWrite();
$result_obj = (array) ($bulk->insert($data));
try{
$r = $this->MongoDBManager->executeBulkWrite(self::STORAGE_ADDRESS, $bulk);
}catch(MongoDB\Driver\Exception\BulkWriteException $e){
return false;
}
if (! is_object($r)){
return false;
}
return true;
}
}

+ 46
- 0
src/protected/model/SampleMySQLStorage.php View File

@ -0,0 +1,46 @@
<?php
/**
* SampleMySQLStorage.php
* Sample MySQL storage model
* @version 160819:1
* @author karminski <code.karminski@outlook.com>
*/
/**
* class SampleMySQLStorage
*/
class SampleMySQLStorage{
const CONNECTION_INFO = 'SampleMySQLStorage';
const TABLE_NAME = 'sample';
public $MySQL;
/**
* [__construct description]
* @param [type] $redis [description]
*/
public function __construct(){
$this->MySQL = SaltFish\MysqlFactory::spawn(self::CONNECTION_INFO);
}
/**
* [set description]
* @param SampleRequest $SampleRequest [description]
*/
public function set(SampleRequest $SampleRequest){
$data = array();
$SampleRequest->exportForSampleMySQLStorageSet($data);
$q = $this->MySQL->insertInto(
self::TABLE_NAME,
$data
);
$r = $q->execute();
return empty($r) ? false : true;
}
}

+ 117
- 0
src/protected/model/SampleRequest.php View File

@ -0,0 +1,117 @@
<?php
/**
* SampleRequest.php
* SampleRequest model.
* @version 170424:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 170424:1 INIT version.
*/
/**
* class SampleRequest
*/
class SampleRequest extends Request{
public $uid;
public $before;
public $after;
public $page;
public $src;
public $token;
/**
* [setRequest description]
* @param [type] &$input [description]
*/
public function setRequest(&$input){
if(!empty($input['after'])){
$this->after = $input['after'];
}elseif(!empty($input['before'])){
$this->before = $input['before'];
}else{
return false;
}
$this->uid = $input['uid'];
$this->page = $input['page'];
$this->token = $input['token'];
$this->src = $input['src'];
return true;
}
/**
* [exportForAccessibilityCheck description]
* @param [type] &$data [description]
* @return [type] [description]
*/
public function exportForAccessibilityCheck(&$data){
$data = [
'uid' => $this->uid,
];
return true;
}
/**
* [isAfterRequest description]
* @return boolean [description]
*/
public function isAfterRequest(){
if(!empty($this->after)) return true;
return false;
}
/**
* [exportForSampleCacheSet description]
* @param [type] &$requestKey [description]
* @param [type] &$requestData [description]
* @return [type] [description]
*/
public function exportForSampleCacheSet(&$requestKey, &$requestData){
$requestKey = "uid";
$requestData = $this->uid;
return true;
}
/**
* [exportForSampleCacheGet description]
* @param [type] &$requestKey [description]
* @return [type] [description]
*/
public function exportForSampleCacheGet(&$requestKey){
$requestKey = $this->uid;
return true;
}
/**
* [exportForSampleMongoDBStorageGet description]
* @param [type] $req [description]
* @return [type] [description]
*/
public function exportForSampleMongoDBStorageGet(&$req){
$req['uid'] = $this->uid;
return true;
}
/**
* [exportForSampleMongoDBStorageSet description]
* @param [type] &$data [description]
* @return [type] [description]
*/
public function exportForSampleMongoDBStorageSet(&$data){
$data['uid'] = $this->uid;
return true;
}
/**
* [exportForSampleMySQLStorageSet description]
* @param [type] &$data [description]
* @return [type] [description]
*/
public function exportForSampleMySQLStorageSet(&$data){
$data['uid'] = $this->uid;
return true;
}
}

+ 64
- 0
src/protected/model/SampleResult.php View File

@ -0,0 +1,64 @@
<?php
/**
* SampleResult.php
* SampleResult model.
* @version 170424:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 170424:1 INIT version.
*/
/**
* class SampleResult
*/
class SampleResult extends Result{
public $data;
public $doc;
public $r;
/**
* [importResultFromCache description]
* @param [type] &$resultData [description]
* @return [type] [description]
*/
public function importResultFromCache(&$resultData){
if(empty($resultData)) return false;
$this->data = json_decode($resultData, true);
return true;
}
/**
* [importFromSampleMongoDBStorageGet description]
* @param [type] &$doc [description]
* @return [type] [description]
*/
public function importFromSampleMongoDBStorageGet(&$doc){
if(empty($doc['uid'])) return false;
$this->doc = $doc;
return true;
}
/**
* [importFromSampleMySQLStorageGet description]
* @param [type] &$r [description]
* @return [type] [description]
*/
public function importFromSampleMySQLStorageGet(&$r){
if(empty($r)) return false;
$this->r = $r;
return true;
}
/**
* [exportForFeedback description]
* @param [type] &$feedData [description]
* @return [type] [description]
*/
public function exportForFeedback(&$feedData){
$feedData = $this->data;
return true;
}
}

+ 92
- 0
src/protected/views/BaseRender.php View File

@ -0,0 +1,92 @@
<?php
/**
* BaseRender.php
* Base render.
* @version 160104:1
* @author karminski <code.karminski@outlook.com>
*
* @changelogs
* 160104:1 INIT commit
*/
namespace SaltFish;
/**
* class BaseRender
*/
class BaseRender{
/**
* [fillBlank description]
* @param [type] $data [description]
* @param integer $max_length [description]
* @return [type] [description]
*/
private static function fillBlank($data = "", $max_length = 0, $separator = false){
$len = strlen($data);
$fillNum = $max_length - $len;
for($i=0;$i<$fillNum;$i++){
if(!$separator){
$data .= ' ';
}else{
$data .= '-';
}
}
if($separator){
return "+-{$data}-";
}else{
return "| {$data} ";
}
}
/**
* [generateGrid description]
* @param [type] $data [description]
* @return [type] [description]
*/
public static function grid(&$r){
$header = "";
$sepLine = "";
$content = array();
$maxLenStat = array();
// stat max len
$firstLine = true;
foreach($r as $lineNum => $line){
foreach($line as $key => $value){
$len = 0;
$len = strlen($value);
if($firstLine){
$keyLen = strlen($key);
$keyLen > $len ? $len = $keyLen : true;
}
if(empty($maxLenStat[$key])){
$maxLenStat[$key] = $len;
continue;
}
if($maxLenStat[$key]<$len) $maxLenStat[$key] = $len;
}
$firstLine = false;
}
// form grid
$headerGenSwitch = true;
foreach($r as $lineNum => $line){
$content[$lineNum] = "";
foreach($line as $key => $value){
if($headerGenSwitch){
$header .= self::fillBlank($key, $maxLenStat[$key]);
$sepLine .= self::fillBlank('', $maxLenStat[$key], true);
}
$content[$lineNum] .= self::fillBlank($value, $maxLenStat[$key]);
}
$content[$lineNum] .= " |\n";
$headerGenSwitch = false;
}
$header .= " |\n";
$sepLine .= "-+\n";
$content = implode('', $content);
return("{$sepLine}{$header}{$sepLine}{$content}{$sepLine}");
}
}

+ 120
- 0
src/protected/views/BaseView.php View File

@ -0,0 +1,120 @@
<?php
/**
* BaseView.php
* Views of Base.
* @version 150811:2
* @author karminski <code.karminski@outlook.com>
*
* @changelogs 150811:1 ADD renderRaw()
* 150511:1 INIT commit
*/
/**
* class BaseView
*/
class BaseView{
/**
* [renderJson description]
* @param [type] $r [description]
* @return [type] [description]
*/
public static function renderJson($r){
header('Content-type: application/json; charset=utf-8');
print($r);
flush();
}
/**
* [renderRaw description]
* @param [type] $r [description]
* @return [type] [description]
*/
public static function renderRaw($r){
var_dump($r);
flush();
}
/**
* [genBlank description]
* @param [type] $data [description]
* @param integer $max_length [description]
* @return [type] [description]
*/
private static function genBlank($data = "", $max_length = 0, $separator = false){
$len = strlen($data);
$fillNum = $max_length - $len;
for($i=0;$i<$fillNum;$i++){
if(!$separator){
$data .= ' ';
}else{
$data .= '-';
}
}
if($separator){
return "+-{$data}-";
}else{
return "| {$data} ";
}
}
/**
* [generateGrid description]
* @param [type] $data [description]
* @return [type] [description]
*/
public static function generateGrid(&$r){
$header = "";
$sepLine = "";
$content = array();
$maxLenStat = array();
// stat max len
$firstLine = true;
foreach($r as $lineNum => $line){
foreach($line as $key => $value){
$len = 0;
$len = strlen($value);
if($firstLine){
$keyLen = strlen($key);
$keyLen > $len ? $len = $keyLen : true;
}
if(empty($maxLenStat[$key])){
$maxLenStat[$key] = $len;
continue;
}
if($maxLenStat[$key]<$len) $maxLenStat[$key] = $len;
}
$firstLine = false;
}
// form grid
$headerGenSwitch = true;
foreach($r as $lineNum => $line){
$content[$lineNum] = "";
foreach($line as $key => $value){
if($headerGenSwitch){
$header .= self::genBlank($key, $maxLenStat[$key]);
$sepLine .= self::genBlank('', $maxLenStat[$key], true);
}
$content[$lineNum] .= self::genBlank($value, $maxLenStat[$key]);
}
$content[$lineNum] .= " |\n";
$headerGenSwitch = false;
}
$header .= " |\n";
$sepLine .= "-+\n";
$content = implode('', $content);
return("{$sepLine}{$header}{$sepLine}{$content}{$sepLine}");
}
/**
* [renderDumpGrid description]
* @param [type] $r [description]
* @return [type] [description]
*/
public static function renderDumpGrid(&$r){
echo self::generateGrid($r);
flush();
}
}
?>

+ 10
- 0
src/status.php View File

@ -0,0 +1,10 @@
<?php
/**
* status.php
* Api entrance
* @version 151024:1
* @author karminski <code.karminski@outlook.com>
*/
echo("ok");

+ 29
- 0
tests/ObjectArray.test.php View File

@ -0,0 +1,29 @@
<?php
/**
* ObjectArray.test.php
* Test Base controller.
* @version 150629:1
* @author karminski <code.karminski@outlook.com>
*
*/
require(dirname(__FILE__).'/../protected/lib/base/ObjectArray.php');
class DefaultRequest extends ObjectArray{
}
$input = array(
'id' => 12,
'type' => 'a',
'callback' => 'json_callback_21453534534',
'src' => 'pc_452345',
);
$DefaultRequest = new DefaultRequest($input);
$DefaultRequest['tar'] = 'zxvf';
var_dump(json_encode($DefaultRequest));

+ 69
- 0
tests/TestBase.php View File

@ -0,0 +1,69 @@
<?php
/**
* TestBase.php
* Test Base controller.
* @version 150629:1
* @author karminski <code.karminski@outlook.com>
*
*/
/**
* abstract class TestBase
*/
abstract class TestBase{
public $_log;
/**
* [__construct description]
*/
public function __construct($requirements){
// require
$this->InitRequirements($requirements);
// check mode
if(ONLINE_MODE){
$debug = false;
}else{
$debug = true;
}
// load files
$this->initLog($debug);
$this->debugSwitch(true);
}
/**
* [debugSwitch description]
* @param boolean $switch [description]
* @return [type] [description]
*/
public function debugSwitch($switch = false){
if($switch){
ini_set('display_errors', 1);
error_reporting(E_ALL);
}
return;
}
/**
* [InitRequirements description]
*/
public function InitRequirements($requirements){
$class_name = get_class($this);
require(dirname(__FILE__).'/../protected/lib/base/AutoRequire.php');
AutoRequire::_class($requirements);
return;
}
/**
* [initLog description]
* @param [type] $debug [description]
* @return [type] [description]
*/
public function initLog($debug){
$this->_log = new Log();
$this->_log->setLogAddress(LOG_FILE, LOG_SPLIT);
$this->_log->setEchoSwitch($debug);
return;
}
}

+ 21
- 0
tests/lib/Config.phpt View File

@ -0,0 +1,21 @@
--TEST--
SaltFish\Config test
@version 160103:1
--FILE--
<?php
require(dirname(__FILE__).'/../../src/protected/lib/base/AutoRequire.php');
require(dirname(__FILE__).'/../../src/protected/lib/base/Config.php');
$main = array(
'database' => 1,
);
SaltFish\Config::importMainConfig($main);
$db = SaltFish\Config::get('database');
var_dump($db);
?>
--EXPECT--
int(1)

+ 17
- 0
tests/lib/Encrypt.test.php View File

@ -0,0 +1,17 @@
<?php
require("Encrypt.class.php");
$pass = "123456ffffffffffffff";
$hash = Encrypt::hash($pass);
var_dump($hash);
$r = Encrypt::match($pass, $hash);
var_dump($r);
if($r){
print("MATCH\n");
}else{
print("NOT MATCH\n");
}

+ 91
- 0
tests/lib/Input.test.php View File

@ -0,0 +1,91 @@
<?php
/**
* Input.test.php
* Test Base controller.
* @version 150629:1
* @author karminski <code.karminski@outlook.com>
*
*/
require(dirname(__FILE__).'/../protected/lib/base/Input.php');
$input_param = array(
array(
'param' => 'id',
'condition' => array(
'method' => null,
'type' => Input::TYPE_INT,
'limit' => array('min' => 0, 'max' => 100000000),
'default' => 0,
'necessary' => true,
),
),
array(
'param' => 'type',
'condition' => array(
'method' => null,
'type' => Input::TYPE_ENUM,
'options' => array("a", "b", "c", 0),
'default' => 0,
'necessary' => true,
),
),
array(
'param' => 'data',
'condition' => array(
'method' => null,
'type' => Input::TYPE_STRING,
'sub_type' => Input::TYPE_STRING,
'limit' => array('fixed' => 32),
'default' => null,
'necessary' => true,
),
),
array(
'param' => 'token',
'condition' => array(
'method' => null,
'type' => Input::TYPE_STRING,
'limit' => array('fixed' => 32),
'default' => '',
'necessary' => true,
),
),
array(
'param' => 'callback',
'condition' => array(
'method' => null,
'type' => Input::TYPE_STRING,
'limit' => array('min' => 1, 'max' => 40),
'default' => '',
'necessary' => false,
),
),
array(
'param' => 'src',
'condition' => array(
'method' => null,
'type' => Input::TYPE_STRING,
'limit' => array('min' => 1, 'max' => 40),
'default' => '',
'necessary' => false,
),
),
);
$_GET = array(
'id' => 12,
'type' => 'a',
'callback' => 'json_callback_21453534534',
'src' => 'pc_452345',
);
$r = array();
$exp = array();
Input::get(Input::METHOD_GET, $input_param, $r, $exp);
var_dump($r);
var_dump($exp);

+ 2929
- 0
tests/run-tests.php
File diff suppressed because it is too large
View File


Loading…
Cancel
Save