Compare commits

..

No commits in common. "6ca8048e1a47954360be2aead52972c5b7ac51e3" and "4367212756e8a4c8a91f8356b0cae7d015b45fe1" have entirely different histories.

712 changed files with 9882 additions and 79689 deletions

View file

@ -1 +0,0 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="24" height="24"><title>Alipay</title><path fill="#1677FF" d="M19.695 15.07c3.426 1.158 4.203 1.22 4.203 1.22V3.846c0-2.124-1.705-3.845-3.81-3.845H3.914C1.808.001.102 1.722.102 3.846v16.31c0 2.123 1.706 3.845 3.813 3.845h16.173c2.105 0 3.81-1.722 3.81-3.845v-.157s-6.19-2.602-9.315-4.119c-2.096 2.602-4.8 4.181-7.607 4.181-4.75 0-6.361-4.19-4.112-6.949.49-.602 1.324-1.175 2.617-1.497 2.025-.502 5.247.313 8.266 1.317a16.796 16.796 0 0 0 1.341-3.302H5.781v-.952h4.799V6.975H4.77v-.953h5.81V3.591s0-.409.411-.409h2.347v2.84h5.744v.951h-5.744v1.704h4.69a19.453 19.453 0 0 1-1.986 5.06c1.424.52 2.702 1.011 3.654 1.333m-13.81-2.032c-.596.06-1.71.325-2.321.869-1.83 1.608-.735 4.55 2.968 4.55 2.151 0 4.301-1.388 5.99-3.61-2.403-1.182-4.438-2.028-6.637-1.809"/></svg>

Before

Width:  |  Height:  |  Size: 840 B

View file

@ -1 +0,0 @@
<?xml version="1.0"?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="512px" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="_x31_3-alipay"><g><path d="M413.858,26.001H98.143c-39.892,0-72.141,32.251-72.141,72.142v315.715 c0,39.89,32.249,72.142,72.141,72.142h315.716c39.551,0,71.628-31.911,72.14-71.463c-47.189-26.308-113.55-61.955-176.194-90.809 c-32.928,45.146-86.394,83.169-152.6,83.169c-72.482,0-96.24-46.513-99.641-78.416c-4.075-40.056,15.279-83.683,102.186-83.683 c36.328,0,81.476,10.524,130.537,25.629c16.972-30.896,27.156-61.956,27.156-61.956H134.471v-17.147h94.546v-32.077H116.645 v-19.519h112.372V87.957h52.284v51.771h112.371v19.519H281.289v32.077h91.148c0,0-15.617,47.868-39.378,93.355 c50.24,17.147,102.688,37.006,152.601,54.151V98.143C485.826,58.417,453.584,26.001,413.858,26.001z M74.547,324.742 c1.018,20.711,10.524,55.171,71.803,55.171c53.464,0,95.061-40.744,121.027-74.852c-45.824-19.182-86.742-32.252-112.372-32.252 C85.749,272.81,73.531,306.752,74.547,324.742z" style="fill:#0099E5;"/></g></g><g id="Layer_1"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none">
<circle cx="8" cy="8" r="7" fill="#873EFF"/>
<path fill="#fff" d="M5.909 10.9c.608 0 1.096-.3 1.464-.991l.819-1.533v1.3c0 .766.496 1.224 1.262 1.224.6 0 1.044-.263 1.472-.991l1.885-3.185c.413-.698.12-1.224-.789-1.224-.488 0-.803.158-1.089.691l-1.3 2.44v-2.17c0-.646-.307-.961-.878-.961-.45 0-.811.195-1.089.736L6.442 8.632V6.484c0-.691-.285-.984-.976-.984H4.054c-.534 0-.804.248-.804.706 0 .458.285.721.804.721h.578v2.741c0 .774.518 1.232 1.277 1.232Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 538 B

View file

@ -1,5 +0,0 @@
{
"require": {
"alipaysdk/easysdk": "^2.0"
}
}

832
composer.lock generated
View file

@ -1,832 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "524ca69e0ade104f4102231471f256e3",
"packages": [
{
"name": "adbario/php-dot-notation",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/adbario/php-dot-notation.git",
"reference": "081e2cca50c84bfeeea2e3ef9b2c8d206d80ccae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/adbario/php-dot-notation/zipball/081e2cca50c84bfeeea2e3ef9b2c8d206d80ccae",
"reference": "081e2cca50c84bfeeea2e3ef9b2c8d206d80ccae",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^5.5 || ^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.7|^6.6|^7.5|^8.5|^9.5",
"squizlabs/php_codesniffer": "^3.6"
},
"type": "library",
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Adbar\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Riku Särkinen",
"email": "riku@adbar.io"
}
],
"description": "PHP dot notation access to arrays",
"homepage": "https://github.com/adbario/php-dot-notation",
"keywords": [
"ArrayAccess",
"dotnotation"
],
"support": {
"issues": "https://github.com/adbario/php-dot-notation/issues",
"source": "https://github.com/adbario/php-dot-notation/tree/2.5.0"
},
"time": "2022-10-14T20:31:46+00:00"
},
{
"name": "alibabacloud/tea",
"version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/aliyun/tea-php.git",
"reference": "1619cb96c158384f72b873e1f85de8b299c9c367"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aliyun/tea-php/zipball/1619cb96c158384f72b873e1f85de8b299c9c367",
"reference": "1619cb96c158384f72b873e1f85de8b299c9c367",
"shasum": ""
},
"require": {
"adbario/php-dot-notation": "^2.4",
"ext-curl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-simplexml": "*",
"ext-xmlwriter": "*",
"guzzlehttp/guzzle": "^6.3|^7.0",
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "*",
"symfony/dotenv": "^3.4",
"symfony/var-dumper": "^3.4"
},
"suggest": {
"ext-sockets": "To use client-side monitoring"
},
"type": "library",
"autoload": {
"psr-4": {
"AlibabaCloud\\Tea\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Alibaba Cloud SDK",
"email": "sdk-team@alibabacloud.com",
"homepage": "http://www.alibabacloud.com"
}
],
"description": "Client of Tea for PHP",
"homepage": "https://www.alibabacloud.com/",
"keywords": [
"alibabacloud",
"client",
"cloud",
"tea"
],
"support": {
"issues": "https://github.com/aliyun/tea-php/issues",
"source": "https://github.com/aliyun/tea-php"
},
"time": "2023-05-16T06:43:41+00:00"
},
{
"name": "alibabacloud/tea-fileform",
"version": "0.3.4",
"source": {
"type": "git",
"url": "https://github.com/alibabacloud-sdk-php/tea-fileform.git",
"reference": "4bf0c75a045c8115aa8cb1a394bd08d8bb833181"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/alibabacloud-sdk-php/tea-fileform/zipball/4bf0c75a045c8115aa8cb1a394bd08d8bb833181",
"reference": "4bf0c75a045c8115aa8cb1a394bd08d8bb833181",
"shasum": ""
},
"require": {
"alibabacloud/tea": "^3.0",
"php": ">5.5"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35|^5.4.3"
},
"type": "library",
"autoload": {
"psr-4": {
"AlibabaCloud\\Tea\\FileForm\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Alibaba Cloud SDK",
"email": "sdk-team@alibabacloud.com"
}
],
"description": "Alibaba Cloud Tea File Library for PHP",
"support": {
"issues": "https://github.com/alibabacloud-sdk-php/tea-fileform/issues",
"source": "https://github.com/alibabacloud-sdk-php/tea-fileform/tree/0.3.4"
},
"time": "2020-12-01T07:24:35+00:00"
},
{
"name": "alipaysdk/easysdk",
"version": "2.2.3",
"source": {
"type": "git",
"url": "https://github.com/alipay/alipay-easysdk.git",
"reference": "c6008839a22a5fca08e9f8536730f7abfed522d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/alipay/alipay-easysdk/zipball/c6008839a22a5fca08e9f8536730f7abfed522d5",
"reference": "c6008839a22a5fca08e9f8536730f7abfed522d5",
"shasum": ""
},
"require": {
"alibabacloud/tea": "^3.1",
"alibabacloud/tea-fileform": "^0.3.2",
"ext-ctype": "*",
"ext-curl": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-simplexml": "*",
"ext-xmlwriter": "*",
"guzzlehttp/guzzle": ">=6.3",
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Alipay\\EasySDK\\": "php/src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "junying.wjy",
"email": "junying.wjy@antfin.com"
}
],
"description": "支付宝官方 Alipay Easy SDK",
"support": {
"source": "https://github.com/alipay/alipay-easysdk/tree/v2.2.3"
},
"time": "2022-11-28T14:04:57+00:00"
},
{
"name": "guzzlehttp/guzzle",
"version": "7.10.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
"reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^2.3",
"guzzlehttp/psr7": "^2.8",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
},
"provide": {
"psr/http-client-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"ext-curl": "*",
"guzzle/client-integration-tests": "3.0.2",
"php-http/message-factory": "^1.1",
"phpunit/phpunit": "^8.5.39 || ^9.6.20",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
"ext-curl": "Required for CURL handler support",
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
"psr/log": "Required for using the Log middleware"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Jeremy Lindblom",
"email": "jeremeamia@gmail.com",
"homepage": "https://github.com/jeremeamia"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
}
],
"description": "Guzzle is a PHP HTTP client library",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"psr-18",
"psr-7",
"rest",
"web service"
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.10.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
"type": "tidelift"
}
],
"time": "2025-08-23T22:36:01+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "481557b130ef3790cf82b713667b43030dc9c957"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957",
"reference": "481557b130ef3790cf82b713667b43030dc9c957",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.44 || ^9.6.25"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/2.3.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
"type": "tidelift"
}
],
"time": "2025-08-22T14:34:08+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.8.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "21dc724a0583619cd1652f673303492272778051"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051",
"reference": "21dc724a0583619cd1652f673303492272778051",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
"psr/http-factory-implementation": "1.0",
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "0.9.0",
"phpunit/phpunit": "^8.5.44 || ^9.6.25"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://sagikazarmark.hu"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.8.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
"type": "tidelift"
}
],
"time": "2025-08-23T21:21:41+00:00"
},
{
"name": "psr/http-client",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
"homepage": "https://github.com/php-fig/http-client",
"keywords": [
"http",
"http-client",
"psr",
"psr-18"
],
"support": {
"source": "https://github.com/php-fig/http-client"
},
"time": "2023-09-23T14:17:50+00:00"
},
{
"name": "psr/http-factory",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": ""
},
"require": {
"php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
"message",
"psr",
"psr-17",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-factory"
},
"time": "2024-04-15T12:06:14+00:00"
},
{
"name": "psr/http-message",
"version": "2.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/2.0"
},
"time": "2023-04-04T09:54:51+00:00"
},
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
"reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5 || ^6.5"
},
"type": "library",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders.",
"support": {
"issues": "https://github.com/ralouphie/getallheaders/issues",
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
},
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.6-dev"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-25T14:21:43+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

View file

@ -39,4 +39,71 @@
.woo-alipay-settings .test-status-message.is-active,
.woo-alipay-settings .description.help.is-active {
display: block;
}

.woo-alipay-config-help {
max-width: 800px;
}

.woo-alipay-config-help ul {
margin-left: 30px;
}

.woo-alipay-config-help li {
list-style: disc;
}

.woo-alipay-config-help .inside {
padding: 0 30px 12px;
}

.woo-alipay-config-help .handle {
border-bottom: 1px solid #ccd0d4;
line-height: 1.4em;
font-size: 14px;
margin: 0;
padding: 8px 12px;
}

.js .woo-alipay-config-help .handlediv {
display: block;
}

.woo-alipay-config-help .handlediv {
display: none;
float: right;
width: 36px;
height: 36px;
margin: 0;
padding: 0;
border: 0;
background: 0 0;
cursor: pointer;
}

.woo-alipay-config-help .handlediv,
.woo-alipay-config-help .handlediv.button-link {
color: #72777c;
}

.woo-alipay-config-help .handlediv .toggle-indicator:before {
margin-top: 4px;
width: 20px;
border-radius: 50%;
text-indent: -1px;
content: "\f142";
display: inline-block;
font: normal 20px/1 dashicons;
speak: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-decoration: none!important;
}

.woo-alipay-config-help.closed .handlediv .toggle-indicator:before {
content: "\f140";
}

.woo-alipay-config-help.closed .inside {
display: none;
}

View file

@ -1,42 +1 @@
.woo-alipay-settings .spinner,
.woo-alipay-settings .test-status {
display: inline-block;
float: none;
vertical-align: middle;
}

.woo-alipay-settings .spinner {
background-size: 20px 20px;
opacity: .7;
filter: alpha(opacity=70);
}

.woo-alipay-settings .test-status .success {
color: #46b450;
}

.woo-alipay-settings .test-status .failure,
.woo-alipay-settings .test-status .error {
color: #dc3232;
}

.woo-alipay-settings .test-status .dashicons {
margin: 4px 10px 8px;
display: none;
}

.woo-alipay-settings .test-status .dashicons.is-active {
display: inline-block;
}

.woo-alipay-settings .test-status-message.success,
.woo-alipay-settings .test-status-message.failure,
.woo-alipay-settings .test-status-message.error,
.woo-alipay-settings .description.help {
display: none;
}

.woo-alipay-settings .test-status-message.is-active,
.woo-alipay-settings .description.help.is-active {
display: block;
}
.woo-alipay-settings .spinner,.woo-alipay-settings .test-status{display:inline-block;float:none;vertical-align:middle}.woo-alipay-settings .spinner{background-size:20px 20px;opacity:.7}.woo-alipay-settings .test-status .success{color:#46b450}.woo-alipay-settings .test-status .error,.woo-alipay-settings .test-status .failure{color:#dc3232}.woo-alipay-settings .test-status .dashicons{margin:4px 10px 8px;display:none}.woo-alipay-settings .test-status .dashicons.is-active{display:inline-block}.woo-alipay-settings .description.help,.woo-alipay-settings .test-status-message.error,.woo-alipay-settings .test-status-message.failure,.woo-alipay-settings .test-status-message.success{display:none}.woo-alipay-settings .description.help.is-active,.woo-alipay-settings .test-status-message.is-active{display:block}.woo-alipay-config-help{max-width:800px}.woo-alipay-config-help ul{margin-left:30px}.woo-alipay-config-help li{list-style:disc}.woo-alipay-config-help .inside{padding:0 30px 12px}.woo-alipay-config-help .handle{border-bottom:1px solid #ccd0d4;line-height:1.4em;font-size:14px;margin:0;padding:8px 12px}.js .woo-alipay-config-help .handlediv{display:block}.woo-alipay-config-help .handlediv{display:none;float:right;width:36px;height:36px;margin:0;padding:0;border:0;background:0 0;cursor:pointer}.woo-alipay-config-help .handlediv,.woo-alipay-config-help .handlediv.button-link{color:#72777c}.woo-alipay-config-help .handlediv .toggle-indicator:before{margin-top:4px;width:20px;border-radius:50%;text-indent:-1px;content:"\f142";display:inline-block;font:20px/1 dashicons;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-decoration:none!important}.woo-alipay-config-help.closed .handlediv .toggle-indicator:before{content:"\f140"}.woo-alipay-config-help.closed .inside{display:none}

View file

@ -1,68 +0,0 @@
<?php

use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType;

final class WC_Alipay_Blocks_Support extends AbstractPaymentMethodType {

private $gateway;
protected $name = 'alipay';

public function __construct() {
$this->name = 'alipay';
}

public function initialize() {
$this->settings = get_option( 'woocommerce_alipay_settings', array() );
$gateways = WC()->payment_gateways->payment_gateways();
$this->gateway = isset( $gateways['alipay'] ) ? $gateways['alipay'] : false;
}

public function is_active() {
$enabled = isset( $this->settings['enabled'] ) ? $this->settings['enabled'] : 'no';
return 'yes' === $enabled;
}

public function get_payment_method_script_handles() {
$script_path = 'js/frontend/blocks.js';
$script_asset_path = WOO_ALIPAY_PLUGIN_PATH . 'js/frontend/blocks.asset.php';
$script_asset = file_exists( $script_asset_path )
? require( $script_asset_path )
: array(
'dependencies' => array( 'wc-blocks-registry', 'wp-element', 'wp-html-entities', 'wp-i18n' ),
'version' => '3.1.0'
);
$script_url = trailingslashit( WOO_ALIPAY_PLUGIN_URL ) . $script_path;

wp_register_script(
'wc-alipay-payments-blocks',
$script_url,
$script_asset['dependencies'],
$script_asset['version'],
true
);

if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'wc-alipay-payments-blocks', 'woo-alipay', WOO_ALIPAY_PLUGIN_PATH . 'languages' );
}

return [ 'wc-alipay-payments-blocks' ];
}

public function get_payment_method_script_handles_for_admin() {
return $this->get_payment_method_script_handles();
}

public function get_payment_method_data() {
return [
'title' => $this->get_setting( 'title', '支付宝' ),
'description' => $this->get_setting( 'description', '通过支付宝付款(中国大陆,包括香港和澳门)。' ),
'supports' => $this->get_supported_features(),
'icon' => WOO_ALIPAY_PLUGIN_URL . 'assets/images/alipay-icon.svg',
];
}

public function get_supported_features() {
return $this->gateway ? $this->gateway->supports : [];
}
}

View file

@ -40,7 +40,6 @@ class WC_Alipay extends WC_Payment_Gateway
$this->method_title = __('Alipay by Woo Alipay', 'woo-alipay');
$this->charset = strtolower(get_bloginfo('charset'));
$this->id = self::GATEWAY_ID;
$this->icon = WOO_ALIPAY_PLUGIN_URL . 'assets/images/alipay-icon.svg';
$this->description = $this->get_option('description');
$this->method_description = __('Alipay is a simple, secure and fast online payment method.', 'woo-alipay');
$this->exchange_rate = $this->get_option('exchange_rate');
@ -56,13 +55,6 @@ class WC_Alipay extends WC_Payment_Gateway
$this->supports = array(
'products',
'refunds',
'subscriptions',
'subscription_cancellation',
'subscription_suspension',
'subscription_reactivation',
'subscription_amount_changes',
'subscription_date_changes',
'multiple_subscriptions',
);

self::$log_enabled = ('yes' === $this->get_option('debug', 'no'));
@ -123,26 +115,18 @@ class WC_Alipay extends WC_Payment_Gateway
'appid' => array(
'title' => __('Alipay App ID', 'woo-alipay'),
'type' => 'text',
'description' => sprintf(
__('在支付宝开放平台获取的应用ID。%s', 'woo-alipay'),
'<a href="https://openhome.alipay.com/platform/developerIndex.htm" target="_blank">' . __('前往支付宝开放平台', 'woo-alipay') . '</a>'
),
'description' => __('The App ID found in Alipay Open Platform', 'woo-alipay'),
),
'public_key' => array(
'title' => __('Alipay public key', 'woo-alipay'),
'type' => 'textarea',
'description' => sprintf(
__('支付宝公钥,在支付宝开放平台应用详情页面获取。%s', 'woo-alipay'),
'<a href="https://woocn.com/product/woo-alipay.html" target="_blank">' . __('查看配置教程', 'woo-alipay') . '</a>'
),
'description' => __('The Alipay public key generated in the Alipay Open Platform ("支付宝公钥").', 'woo-alipay'),
),
'private_key' => array(
'title' => __('Alipay Merchant application private key', 'woo-alipay'),
'type' => 'textarea',
'description' => sprintf(
__('应用私钥使用支付宝密钥生成工具或openssl命令生成。<br/><strong>此密钥为机密信息,请勿泄露给任何人</strong>。%s', 'woo-alipay'),
'<a href="https://woocn.com/product/woo-alipay.html" target="_blank">' . __('查看密钥生成教程', 'woo-alipay') . '</a>'
),
'description' => __('The private key generated with the provided Alipay tool application or the <code>openssl</code> command line.<br/>
This key is secret and is not recorded in Alipay Open Platform - <strong>DO NOT SHARE THIS VALUE WITH ANYONE</strong>.', 'woo-alipay'),
),
'sandbox' => array(
'title' => __('Sandbox', 'woo-alipay'),
@ -185,9 +169,9 @@ class WC_Alipay extends WC_Payment_Gateway

protected function is_wooalipay_enabled()
{
$alipay_options = get_option('woocommerce_alipay_settings', array());
$alipay_options = get_option('woocommerce_alipay_settings');

return isset($alipay_options['enabled']) && ('yes' === $alipay_options['enabled']);
return ('yes' === $alipay_options['enabled']);
}

public function validate_settings()
@ -217,15 +201,8 @@ class WC_Alipay extends WC_Payment_Gateway
{
$is_available = ('yes' === $this->enabled) ? true : false;

if (!$is_available) {
return false;
}

if ('yes' === $this->get_option('sandbox')) {
return true;
}

if ($this->multi_currency_enabled) {

if (
!in_array(get_woocommerce_currency(), $this->supported_currencies, true) &&
!$this->exchange_rate
@ -275,9 +252,12 @@ class WC_Alipay extends WC_Payment_Gateway
$order = new WC_Order($order_id);

if (!$this->can_refund_order($order)) {
return new WP_Error('error', __('Refund failed', 'woocommerce') . ' - ' . $this->refundable_status['reason']);

return new WP_Error('error', __('Refund failed', 'woocommerce') . ' - ' . $this->refund_status['reason']);
}

Woo_Alipay::require_lib('refund');

$trade_no = $order->get_transaction_id();
$total = $this->maybe_convert_amount($order->get_total());
$amount = $this->maybe_convert_amount($amount);
@ -354,23 +334,29 @@ class WC_Alipay extends WC_Payment_Gateway

protected function do_refund($out_trade_no, $trade_no, $amount, $refund_id, $reason, $order_id = 0)
{
\Alipay\EasySDK\Kernel\Factory::setOptions($this->get_easy_sdk_config());
$refund_request_builder = new AlipayTradeRefundContentBuilder();

try {
$response = \Alipay\EasySDK\Kernel\Factory::payment()->common()->refund($out_trade_no, $amount, $reason, $refund_id);
$responseChecker = new \Alipay\EasySDK\Kernel\Util\ResponseChecker();
if ($responseChecker->success($response)) {
self::log(__METHOD__ . ' Refund Result: ' . wc_print_r($response, true));
return $response;
} else {
self::log(__METHOD__ . ' Refund Error: ' . wc_print_r($response, true));
return new WP_Error('error', $response->msg . '; ' . $response->subMsg);
}
} catch (Exception $e) {
self::log(__METHOD__ . ' Refund Exception: ' . $e->getMessage(), 'error');
return new WP_Error('error', $e->getMessage());
$refund_request_builder->setOutTradeNo($out_trade_no);
$refund_request_builder->setTradeNo($trade_no);
$refund_request_builder->setRefundAmount($amount);
$refund_request_builder->setOutRequestNo($refund_id);
$refund_request_builder->setRefundReason(esc_html($reason));

$config = $this->get_config($order_id);
$aop = new AlipayTradeService($config);
$response = $aop->Refund($refund_request_builder);

if (10000 !== absint($response->code)) {
self::log(__METHOD__ . ' Refund Error: ' . wc_print_r($response, true));

$result = new WP_Error('error', $response->msg . '; ' . $response->sub_msg);
} else {
self::log(__METHOD__ . ' Refund Result: ' . wc_print_r($response, true));

$result = $response;
}

return $result;
}

protected function get_config($order_id = 0)
@ -390,41 +376,16 @@ class WC_Alipay extends WC_Payment_Gateway
return $config;
}

protected function get_easy_sdk_config()
protected static function log($message, $level = 'info', $force = false)
{
$config = new \Alipay\EasySDK\Kernel\Config();
$config->protocol = 'https';
$config->gatewayHost = ('yes' === $this->get_option('sandbox')) ? 'openapi.alipaydev.com' : 'openapi.alipay.com';
$config->signType = 'RSA2';
$config->appId = $this->get_option('appid');
$config->merchantPrivateKey = $this->get_option('private_key');
$config->alipayPublicKey = $this->get_option('public_key');
$config->notifyUrl = $this->notify_url;
return $config;
}

protected static function log($message, $level = 'info', $force = false, $context = array())
{
if (self::$log_enabled || $force) {

if (empty(self::$log)) {
self::$log = wc_get_logger();
}

$default_context = array(
'source' => self::GATEWAY_ID,
'timestamp' => current_time('mysql'),
'user_id' => get_current_user_id(),
'ip_address' => WC_Geolocation::get_ip_address(),
);

$context = array_merge($default_context, $context);
if (is_array($message) || is_object($message)) {
$message = wc_print_r($message, true);
}

self::$log->log($level, $message, $context);
self::$log->log($level, $message, array('source' => self::GATEWAY_ID));
}
}

@ -462,42 +423,78 @@ class WC_Alipay extends WC_Payment_Gateway
$order = new WC_Order($order_id);

if (!$order || $order->is_paid()) {

return;
}

$total = $this->maybe_convert_amount($order->get_total());
$out_trade_no = 'WooA' . $order_id . '-' . current_time('timestamp');
$subject = $this->get_order_title($order);
$return_url = $this->get_return_url($order);
Woo_Alipay::require_lib($this->is_mobile() ? 'payment_mobile' : 'payment_computer');

\Alipay\EasySDK\Kernel\Factory::setOptions($this->get_easy_sdk_config());

try {
if ($this->is_mobile()) {
$result = \Alipay\EasySDK\Kernel\Factory::payment()->wap()->pay($subject, $out_trade_no, $total, '', $return_url);
} else {
$result = \Alipay\EasySDK\Kernel\Factory::payment()->page()->pay($subject, $out_trade_no, $total, $return_url);
}

if (isset($result->body)) {
echo $result->body;
return;
}
} catch (Exception $e) {
self::log('Easy SDK Payment Error: ' . $e->getMessage(), 'error', true);
if ($result instanceof WP_Error) {
self::log(__METHOD__ . ' Order #' . $order_id . ': ' . wc_print_r($result));
}

$total = $this->maybe_convert_amount($order->get_total());

if ($this->is_mobile()) {
$pay_request_builder = new AlipayTradeWapPayContentBuilder();
} else {
$pay_request_builder = new AlipayTradePagePayContentBuilder();
}

$pay_request_builder->setBody($this->get_order_title($order, true));
$pay_request_builder->setSubject($this->get_order_title($order));
$pay_request_builder->setTotalAmount($total);
$pay_request_builder->setOutTradeNo('WooA' . $order_id . '-' . current_time('timestamp'));

if ($this->is_mobile()) {
$pay_request_builder->setTimeExpress('15m');
}

$config = $this->get_config($order_id);
$aop = new AlipayTradeService($config);
$dispatcher_form = false;
global $wpdb;

try {
$order->add_meta_data('alipay_initalRequest', $out_trade_no, true);
$order->save();
ob_start();

set_query_var('dispatcher_form', $result->body);
if ($this->is_mobile()) {
$html = $aop->wapPay($pay_request_builder, $config['return_url'], $config['notify_url']);
} else {
$html = $aop->pagePay($pay_request_builder, $config['return_url'], $config['notify_url']);
}

$dom = new DOMDocument();
$dom->loadHTML($html);
$forms = $dom->getElementsByTagName('form');
$payUrl = '';
foreach ($forms as $form) {
$action = $form->getAttribute('action');
if (strpos($action, 'https://openapi.alipay.com/gateway.do') !== false) {
$payUrl = $action;
$inputs = $form->getElementsByTagName('input');
foreach ($inputs as $input) {
$name = $input->getAttribute('name');
$value = urlencode($input->getAttribute('value'));
$params[$name] = $value;
if (!empty($name)) {
$payUrl .= "&$name=$value";
}
}
break;
}
}
$order->add_meta_data('alipay_initalRequest', $payUrl, true);
$order->save();
//$order->add_meta_data('alipay_initalRequest', $r['alipay_trade_page_pay_response']['pay_url'], true);

set_query_var('dispatcher_form', ob_get_clean());

} catch (Exception $e) {
ob_end_clean();

$message = ' Caught an exception when trying to generate the Alipay redirection form: ';

self::log(__METHOD__ . $message . wc_print_r($e, true), 'error');
$order->update_status('failed', $e->getMessage());
WC()->cart->empty_cart();
@ -566,16 +563,19 @@ class WC_Alipay extends WC_Payment_Gateway
{
echo '<h3>' . esc_html(__('Alipay payment gateway by Woo Alipay', 'woo-alipay')) . '</h3>';
echo '<p>' . esc_html(__('Alipay is a simple, secure and fast online payment method.', 'woo-alipay')) . '</p>';
echo '<div class="notice notice-info inline">';
echo '<p>';
printf(
__('需要配置帮助?请查看 %1$s 获取详细的配置指南和文档。', 'woo-alipay'),
'<a href="https://woocn.com/document/woo-alipay" target="_blank">' . __('官方文档', 'woo-alipay') . '</a>'
);
echo '</p>';
echo '</div>';

$url_data = wp_parse_url(get_home_url());
$scheme = $url_data['scheme'];
$url_root = $scheme . '://' . $url_data['host'];
$wc_callback = $this->notify_url;

ob_start();

require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/templates/admin/config-help.php';

$html = ob_get_clean();

echo $html; // WPCS: XSS OK
echo '<table class="form-table woo-alipay-settings">';

$this->generate_settings_html();
@ -594,34 +594,30 @@ class WC_Alipay extends WC_Payment_Gateway
public function check_alipay_response()
{
$response_data = $_POST; // @codingStandardsIgnoreLine
$out_trade_no = isset($_POST['out_trade_no']) ? sanitize_text_field($_POST['out_trade_no']) : '';
$response_app_id = isset($_POST['app_id']) ? sanitize_text_field($_POST['app_id']) : '';
$trade_status = isset($_POST['trade_status']) ? sanitize_text_field($_POST['trade_status']) : '';
$transaction_id = isset($_POST['trade_no']) ? sanitize_text_field($_POST['trade_no']) : '';
$response_total = isset($_POST['total_amount']) ? sanitize_text_field($_POST['total_amount']) : '';
$fund_bill_list = isset($_POST['fund_bill_list']) ? stripslashes(sanitize_text_field($_POST['fund_bill_list'])) : '';
$out_trade_no = filter_input(INPUT_POST, 'out_trade_no', FILTER_SANITIZE_STRING);
$response_app_id = filter_input(INPUT_POST, 'app_id', FILTER_SANITIZE_STRING);
$trade_status = filter_input(INPUT_POST, 'trade_status', FILTER_SANITIZE_STRING);
$transaction_id = filter_input(INPUT_POST, 'trade_no', FILTER_SANITIZE_STRING);
$response_total = filter_input(INPUT_POST, 'total_amount', FILTER_SANITIZE_STRING);
$fund_bill_list = stripslashes(filter_input(INPUT_POST, 'fund_bill_list', FILTER_SANITIZE_STRING));
$needs_reply = false;
$error = false;
$out_trade_no_parts = explode('-', str_replace('WooA', '', $out_trade_no));
$order_id = absint(array_shift($out_trade_no_parts));
$order = wc_get_order($order_id);
$order_check = ($order instanceof WC_Order);
if (!$order_check) {
self::log(__METHOD__ . ' Order #' . $order_id . ' not found.', 'error');
return;
}
$config = $this->get_config($order_id);
$response_data['fund_bill_list'] = stripslashes($response_data['fund_bill_list']);
$order_total = $this->maybe_convert_amount($order->get_total());
$total_amount_check = ($order_total === $response_total);
$response_app_id_check = ($response_app_id === $config['app_id']);
$order_check = ($order instanceof WC_Order);

$order->add_meta_data('alipay_initalResponse', json_encode($_POST), true);

\Alipay\EasySDK\Kernel\Factory::setOptions($this->get_easy_sdk_config());
$result_check_signature = \Alipay\EasySDK\Kernel\Factory::payment()->common()->verifyNotify($_POST);
Woo_Alipay::require_lib('check_notification');

$aop = new AlipayTradeService($config);
$result_check_signature = $aop->check($response_data);

self::log(__METHOD__ . ' Alipay response raw data: ' . wc_print_r($response_data, true));

@ -777,50 +773,13 @@ class WC_Alipay extends WC_Payment_Gateway

public function process_payment($order_id)
{
try {
$order = wc_get_order($order_id);
if (!$order) {
throw new Exception(__('订单不存在', 'woo-alipay'));
}
$order = new WC_Order($order_id);

if (!$this->validate_currency($order)) {
throw new Exception(__('不支持的货币类型', 'woo-alipay'));
}

$order->update_status('pending', __('等待支付宝支付', 'woo-alipay'));

$redirect = $order->get_checkout_payment_url(true);
return array(
'result' => 'success',
'redirect' => $redirect,
);
} catch (Exception $e) {
wc_add_notice($e->getMessage(), 'error');
$this->log('Payment processing error: ' . $e->getMessage());
return array(
'result' => 'failure',
'messages' => $e->getMessage(),
);
}
}

private function validate_currency($order)
{
$currency = $order->get_currency();
if (in_array($currency, $this->supported_currencies, true)) {
return true;
}
if ($this->requires_exchange_rate() && $this->exchange_rate > 0) {
return true;
}
return false;
$redirect = $order->get_checkout_payment_url(true);
return array(
'result' => 'success',
'redirect' => $redirect,
);
}

public function test_connection()
@ -848,21 +807,30 @@ class WC_Alipay extends WC_Payment_Gateway

protected function execute_dummy_query()
{
\Alipay\EasySDK\Kernel\Factory::setOptions($this->get_easy_sdk_config());
Woo_Alipay::require_lib('dummy_query');

$config = $this->get_config();
$aop = new AlipayTradeService($config);
$biz_content = '{"out_trade_no":"00000000000000000"}';
$request = new AlipayTradeQueryRequest();

$request->setBizContent($biz_content);

$response = $aop->aopclientRequestExecute($request);
$response = $response->alipay_trade_query_response;

if (
is_object($response) &&
isset($response->code, $response->sub_code) &&
'40004' === $response->code &&
'ACQ.TRADE_NOT_EXIST' === $response->sub_code
) {
self::log(__METHOD__ . ': ' . 'Dummy query to Alipay successful');

return true;
} else {
self::log(__METHOD__ . ': ' . wc_print_r($response, true));

try {
$response = \Alipay\EasySDK\Kernel\Factory::payment()->common()->query('00000000000000000');
if (isset($response->code) && '40004' === $response->code &&
isset($response->subCode) && 'ACQ.TRADE_NOT_EXIST' === $response->subCode) {
self::log(__METHOD__ . ': ' . 'Dummy query to Alipay successful');
return true;
} else {
self::log(__METHOD__ . ': ' . wc_print_r($response, true));
return false;
}
} catch (Exception $e) {
self::log(__METHOD__ . ': Exception - ' . $e->getMessage(), 'error');
return false;
}
}

View file

@ -28,9 +28,6 @@ class Woo_Alipay {
add_filter( 'woocommerce_get_order_item_totals', array( $this, 'display_order_meta_for_customer' ), 10, 2 );
// Add Alipay orphan transactions email notification
add_filter( 'woocommerce_email_classes', array( $this, 'add_orphan_transaction_woocommerce_email' ), 10, 1 );
// Add WooCommerce Blocks support
add_action( 'init', array( $this, 'woocommerce_gateway_alipay_woocommerce_block_support' ), 1 );
}
}

@ -182,19 +179,4 @@ class Woo_Alipay {
return $email_classes;
}

public function woocommerce_gateway_alipay_woocommerce_block_support() {
if ( ! class_exists( 'Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType' ) ) {
return;
}

require_once WOO_ALIPAY_PLUGIN_PATH . 'inc/class-wc-alipay-blocks-support.php';
add_action(
'woocommerce_blocks_payment_method_type_registration',
function( Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $payment_method_registry ) {
$payment_method_registry->register( new WC_Alipay_Blocks_Support() );
},
5
);
}
}

View file

@ -0,0 +1,398 @@
<?php if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}

?>
<div class="woo-alipay-config-help stuffbox closed">
<button type="button" class="handlediv" aria-expanded="true"><span class="screen-reader-text"><?php esc_html_e( 'Toggle panel: Gateway configuration help', 'woo-alipay' ); ?></span><span class="toggle-indicator" aria-hidden="true"></span></button>
<h2 class="handle">
<?php esc_html_e( 'Gateway configuration help', 'woo-alipay' ); ?>
</h2>
<div class="inside">
<h3>
<?php esc_html_e( 'Configuration process overview:', 'woo-alipay' ); ?>
</h3>
<ul>
<li>
<?php
printf(
// translators: %1$s is <a target="_blank" href="[url]">the Alipay Open Platform</a>
esc_html__( 'Go to the %1$s and log in.', 'woo-alipay' ),
sprintf(
'<a target="_blank" href="%s">%s</a>',
'https://openhome.alipay.com/platform/developerIndex.htm',
esc_html__( 'Alipay Open Platform', 'woo-alipay' )
)
);
?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>url</code>
esc_html__( 'If necessary, create a new app by following the options under the create application "创建应用" menu: website & mobile application "网页&移动应用" > payment access "支付接入". The application type "应用类型" should be web page application "网页应用" and the website URL "网址url" should be %1$s. Creating an app will require one or two working day(s) audit of your website content by Alipay.', 'woo-alipay' ),
sprintf(
'<code>%s</code>',
esc_html( trailingslashit( get_home_url() ) )
)
);
?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>url with placeholder</code>, %2$s is <code>placeholder</code>
esc_html__( 'Once the app is created, go to the app information page - accessible directly via %1$s (replace %2$s with the App ID to be used by Woo Alipay).', 'woo-alipay' ),
sprintf(
'<code>%s</code>',
'https://openhome.alipay.com/platform/appManage.htm#/app/[[YOUR_APP_ID]]/appInfo'
),
sprintf(
'<code>%s</code>',
'[[YOUR_APP_ID]]'
)
);
?>
</li>
<li>
<?php esc_html_e( 'Activate payment features and configure the app with the gateway information (see "Register features, website URL and callback" below).', 'woo-alipay' ); ?>
</li>
<li>
<?php esc_html_e( 'If necessary, generate the application public and private keys, register the application public key in the Alipay Open Platform, and generate the Alipay public key (see "Key management" below).', 'woo-alipay' ); ?>
</li>
<li>
<?php esc_html_e( 'Fill in the configuration fields below.', 'woo-alipay' ); ?>
</li>
</ul>
<h3>
<?php esc_html_e( 'Register features, website URL and callback', 'woo-alipay' ); ?>
</h3>
<p>
<?php esc_html_e( 'To communicate with the payment gateway, Alipay needs some features activated, to know your website URL, and to be aware of the WooCommerce gateway callback endpoint.', 'woo-alipay' ); ?>
</p>
<ul>
<li>
<?php
printf(
// translators: %1$s is <code>url with placeholder</code>, %2$s is <code>placeholder</code>
esc_html__( 'Go to the app overview page - accessible via %1$s (replace %2$s with the App ID to be used by Woo Alipay).', 'woo-alipay' ),
sprintf(
'<code>%s</code>',
'https://openhome.alipay.com/platform/appManage.htm#/app/[[YOUR_APP_ID]]/overview'
),
sprintf(
'<code>%s</code>',
'[[YOUR_APP_ID]]'
)
);
?>
</li>
<li>
<?php
printf(
// translators: %1$s is <a target="_blank" href="[url]">Computer website payment "电脑网站支付"</a>, %2$s is <a target="_blank" href="[url]">Mobile website payment "手机网站支付"</a>
esc_html__( 'Click the add feature button "添加功能" and add the computer website payment "电脑网站支付" and the mobile website payment "手机网站支付" features - these features need to have been activated beforehand (process not describe in this guide - see the Payment products "支付产品" %1$s and %2$s).', 'woo-alipay' ),
sprintf(
'<a target="_blank" href="%s">%s</a>',
'https://b.alipay.com/signing/productDetailV2.htm?productId=I1011000290000001000',
esc_html__( 'Computer website payment "电脑网站支付"', 'woo-alipay' )
),
sprintf(
'<a target="_blank" href="%s">%s</a>',
'https://b.alipay.com/signing/productDetailV2.htm?productId=I1011000290000001001',
esc_html__( 'Mobile website payment "手机网站支付"', 'woo-alipay' )
)
);
?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>url with placeholder</code>, %2$s is <code>placeholder</code>
esc_html__( 'Go to the app information page - accessible via %1$s (replace %2$s with the App ID to be used by Woo Alipay).', 'woo-alipay' ),
sprintf(
'<code>%s</code>',
'https://openhome.alipay.com/platform/appManage.htm#/app/[[YOUR_APP_ID]/appInfo'
),
sprintf(
'<code>%s</code>',
'[[YOUR_APP_ID]]'
)
);
?>
</li>
<li>
<?php esc_html_e( 'Click the setup link "设置" (or modify link "修改") of the application gateway "应用网关" field.', 'woo-alipay' ); ?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>url</code>
esc_html__( 'In the application gateway "应用网关" modal, enter %1$s and click the confirm "确定" button.', 'woo-alipay' ),
sprintf(
'<code>%s</code>',
esc_html( $url_root )
)
);
?>
</li>
<li>
<?php esc_html_e( 'Click the setup link "设置" (or modify link "修改") of the authorization callback address "授权回调地址" field.', 'woo-alipay' ); ?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>scheme</code>, %2$s is <code>url</code>
esc_html__( 'In the authorization callback address "授权回调地址" modal, enter %1$s in the callback address type "回调地址类型" field, %2$s in the callback address "回调地址" field, and leave the verify domain only "只验证域名" checkbox unchecked for added security, then click the confirm "确定" button.', 'woo-alipay' ),
sprintf(
'<code>%s</code>',
esc_html( $scheme )
),
sprintf(
'<code>%s</code>',
esc_html( $wc_callback )
)
);
?>
</li>
</ul>
<h3>
<?php esc_html_e( 'Key management', 'woo-alipay' ); ?>
</h3>
<h4>
<?php esc_html_e( '1 - Generating keys:', 'woo-alipay' ); ?>
</h4>
<p>
<?php
printf(
// translators: %1$s is <strong>application public key</strong>, %2$s is <strong>application private key</strong>, %3$s is <strong>Alipay public key</strong>
esc_html__( 'To complete the payment gateway configuration, you need to generate an %1$s, an %2$s and an %3$s.', 'woo-alipay' ),
sprintf(
'<strong>%s</strong>',
esc_html__( 'application public key', 'woo-alipay' )
),
sprintf(
'<strong>%s</strong>',
esc_html__( 'application private key', 'woo-alipay' )
),
sprintf(
'<strong>%s</strong>',
esc_html__( 'Alipay public key', 'woo-alipay' )
)
);
?>
<p>
<hr/>
<p>
<?php
printf(
// translators: %1$s is <strong>(option 1)</strong>
esc_html__( 'Generate the application keys with the Alipay Open Platform Development Assistant application %1$s:', 'woo-alipay' ),
sprintf(
'<strong>%s</strong>',
esc_html__( '(option 1)', 'woo-alipay' )
)
);
?>
</p>
<ul>
<li>
<?php
printf(
// translators: %1$s is <a target="_blank" href="[url]">Alipay tools documentation page</a>
esc_html__( 'Go to the %1$s and download the Alipay Open Platform Development Assistant application for the operating system of your choice (Windows or MacOS).', 'woo-alipay' ),
sprintf(
'<a target="_blank" href="%s">%s</a>',
'https://docs.open.alipay.com/291/106097/',
esc_html__( 'Alipay tools documentation page', 'woo-alipay' )
)
);
?>
</li>
<li>
<?php esc_html_e( 'Within the Alipay Open Platform Development Assistant application, in the "生成密钥" section (default screen when opening the Assistant), select "UTF-8" and "PKCS1非Java使用" options and click the "生成密钥" button to populate the text areas.', 'woo-alipay' ); ?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>alipay_app_public_key.txt</code>
esc_html__( 'Copy the application public key displayed in the "应用公钥" text area in a text file on your computer (referred to as %1$s below).', 'woo-alipay' ),
'<code>alipay_app_public_key.txt</code>'
);
?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>alipay_app_private_key.txt</code>, %2$s is <strong>DO NOT SHARE THIS VALUE WITH ANYONE</strong>
esc_html__( 'Copy the application private key displayed in the "应用私钥" text area in a text file on your computer (referred to as %1$s below) - %2$s.', 'woo-alipay' ),
'<code>alipay_app_private_key.txt</code>',
sprintf(
'<strong>%s</strong>',
esc_html__( 'DO NOT SHARE THIS VALUE WITH ANYONE', 'woo-alipay' )
)
);
?>
</li>
</ul>
<hr/>
<p>
<?php
printf(
// translators: %1$s is <code>openssl</code>, %2$s is <strong>(option 2)</strong>
esc_html__( 'Generate the application keys with %1$s command line in a terminal %2$s:', 'woo-alipay' ),
'<code>openssl</code>',
sprintf(
'<strong>%s</strong>',
esc_html__( '(option 2)', 'woo-alipay' )
)
);
?>
</p>
<ul>
<li>
<?php
printf(
// translators: %1$s is <code>openssl</code>
esc_html__( 'Type %1$s to open the OpenSSL command line tool.', 'woo-alipay' ),
'<code>openssl</code>'
);
?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>genrsa -out alipay_app_private_key.txt 2048</code>, %2$s is <code>alipay_app_private_key.txt</code>, %3$s is <strong>DO NOT SHARE THIS VALUE WITH ANYONE</strong>
esc_html__( 'Type %1$s to generate the application private key file (referred to as %2$s below) - %3$s.', 'woo-alipay' ),
'<code>genrsa -out alipay_app_private_key.txt 2048</code>',
'<code>alipay_app_private_key.txt</code>',
sprintf(
'<strong>%s</strong>',
esc_html__( 'DO NOT SHARE THIS VALUE WITH ANYONE', 'woo-alipay' )
)
);
?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>rsa -in alipay_app_private_key.txt -pubout -out alipay_app_public_key.txt</code>, %2$s is <code>alipay_app_public_key.txt</code>
esc_html__( 'Type %1$s to generate the application public key file (referred to as %2$s below).', 'woo-alipay' ),
'<code>rsa -in alipay_app_private_key.txt -pubout -out alipay_app_public_key.txt</code>',
'<code>alipay_app_public_key.txt</code>'
);
?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>openssl</code>
esc_html__( 'Type %1$s to quit the command line tool.', 'woo-alipay' ),
'<code>exit</code>'
);
?>
</li>
<li>
<?php esc_html_e( 'Open both files with a text editor, remove all the header, footer, space and carriage return characters to have each key as a single-line long string, and save the files.', 'woo-alipay' ); ?>
</li>
</ul>
<hr/>
<p>
<?php
printf(
// translators: %1$s is <strong>(requires completing option 1 or option 2 above)</strong>
esc_html__( 'Register the application public key in Alipay Open Platform and generate the Alipay public key %1$s:', 'woo-alipay' ),
sprintf(
'<strong>%s</strong>',
esc_html__( '(requires completing option 1 or option 2 above)', 'woo-alipay' )
)
);
?>
</p>
<ul>
<li>
<?php
printf(
// translators: %1$s is <code>url with placeholder</code>, %2$s is <code>placeholder</code>
esc_html__( 'Go to the app information page - accessible via %1$s (replace %2$s with the App ID to be used by Woo Alipay).', 'woo-alipay' ),
sprintf(
'<code>%s</code>',
'https://openhome.alipay.com/platform/appManage.htm#/app/[[YOUR_APP_ID]/appInfo'
),
sprintf(
'<code>%s</code>',
'[[YOUR_APP_ID]]'
)
);
?>
</li>
<li>
<?php esc_html_e( 'Click the link "接口加签方式" > "设置/查看" to open the configuration modal.', 'woo-alipay' ); ?>
</li>
<li>
<?php esc_html_e( 'Use the associated phone number or password to authenticate.', 'woo-alipay' ); ?>
</li>
<li>
<?php esc_html_e( 'In the signature key configuration form (加签管理 | 1 - 加签内容配置), select the public key option "公钥".', 'woo-alipay' ); ?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>alipay_app_public_key.txt</code>
esc_html__( 'Paste the content of the previously saved file %1$s in the "填写公钥字符" text area.', 'woo-alipay' ),
'<code>alipay_app_public_key.txt</code>'
);
?>
</li>
<li>
<?php esc_html_e( 'Click the "保存设置" button to register the application public key and generate the Alipay public key.', 'woo-alipay' ); ?>
</li>
</ul>
<hr/>
<h4>
<?php esc_html_e( '2 - Using the application private key and finding the Alipay public key:', 'woo-alipay' ); ?>
</h4>
<ul>
<li>
<?php
printf(
// translators: %1$s is <code>url with placeholder</code>, %2$s is <code>placeholder</code>
esc_html__( 'Go to the app information page - accessible via %1$s (replace %2$s with the App ID to be used by Woo Alipay).', 'woo-alipay' ),
sprintf(
'<code>%s</code>',
'https://openhome.alipay.com/platform/appManage.htm#/app/[[YOUR_APP_ID]/appInfo'
),
sprintf(
'<code>%s</code>',
'[[YOUR_APP_ID]]'
)
);
?>
</li>
<li>
<?php esc_html_e( 'Click the link "接口加签方式" > "设置/查看" to open the configuration modal.', 'woo-alipay' ); ?>
</li>
<li>
<?php esc_html_e( 'If necessary, use the associated Alipay account\'s phone number or password to authenticate.', 'woo-alipay' ); ?>
</li>
<li>
<?php esc_html_e( 'In the signature completed configuration form (加签管理 | 2 - 加签配置完成), copy the Alipay public key displayed under "支付宝公钥".', 'woo-alipay' ); ?>
</li>
<li>
<?php esc_html_e( 'Paste the Alipay public key in the "Alipay public key" field below.', 'woo-alipay' ); ?>
</li>
<li>
<?php
printf(
// translators: %1$s is <code>alipay_app_private_key.txt</code>
esc_html__( 'Paste the content of the previously saved file %1$s in the "Alipay merchant application private key" field below.', 'woo-alipay' ),
'<code>alipay_app_private_key.txt</code>'
);
?>
</li>
<li>
<?php esc_html_e( 'Click the "Save changes" button.', 'woo-alipay' ); ?>
</li>
</ul>
</div>
</div>

View file

@ -49,6 +49,10 @@ jQuery( document ).ready( function( $ ) {
} );
} );

$('.woo-alipay-config-help .handlediv, .woo-alipay-config-help .handle').on('click', function(e){
e.preventDefault();

$('.woo-alipay-config-help').toggleClass('closed');
});

} );

55
js/admin/main.min.js vendored
View file

@ -1,54 +1 @@
/* global WooAlipay */
jQuery( document ).ready( function( $ ) {

$( '#woo_alipay_test_connection' ).on( 'click', function( e ) {
e.preventDefault();

var spinner = $('.woo-alipay-settings .spinner'),
failure = $('.woo-alipay-settings .test-status .failure, .woo-alipay-settings .test-status-message.failure'),
error = $('.woo-alipay-settings .test-status .error, .woo-alipay-settings .test-status-message.error'),
success = $('.woo-alipay-settings .test-status .success, .woo-alipay-settings .test-status-message.success'),
help = $('.woo-alipay-settings .description.help'),
data = {
nonce : $('#woo_alipay_nonce').val(),
action: 'woo_alipay_test_connection'
};

spinner.addClass('is-active');
failure.removeClass('is-active');
error.removeClass('is-active');
success.removeClass('is-active');

$.ajax( {
url: WooAlipay.ajax_url,
type: 'POST',
data: data
} ).done( function( response ) {
spinner.removeClass('is-active');
window.console.log( response );

if ( response.success ) {
success.addClass('is-active');
help.removeClass('is-active');
} else {

if ( response.data ) {
error.addClass('is-active');
help.removeClass('is-active');
} else {
failure.addClass('is-active');
help.removeClass('is-active');
}
}
} ).fail( function( qXHR, textStatus ) {
WooAlipay.debug && window.console.log( textStatus );
spinner.removeClass('is-active');
success.removeClass('is-active');
help.removeClass('is-active');
failure.addClass('is-active');
} );
} );



} );
jQuery(document).ready(function(n){n("#woo_alipay_test_connection").on("click",function(s){s.preventDefault();var e=n(".woo-alipay-settings .spinner"),t=n(".woo-alipay-settings .test-status .failure, .woo-alipay-settings .test-status-message.failure"),a=n(".woo-alipay-settings .test-status .error, .woo-alipay-settings .test-status-message.error"),i=n(".woo-alipay-settings .test-status .success, .woo-alipay-settings .test-status-message.success"),o=n(".woo-alipay-settings .description.help"),l={nonce:n("#woo_alipay_nonce").val(),action:"woo_alipay_test_connection"};e.addClass("is-active"),t.removeClass("is-active"),a.removeClass("is-active"),i.removeClass("is-active"),n.ajax({url:WooAlipay.ajax_url,type:"POST",data:l}).done(function(s){e.removeClass("is-active"),window.console.log(s),s.success?i.addClass("is-active"):s.data?a.addClass("is-active"):t.addClass("is-active"),o.removeClass("is-active")}).fail(function(s,a){WooAlipay.debug&&window.console.log(a),e.removeClass("is-active"),i.removeClass("is-active"),o.removeClass("is-active"),t.addClass("is-active")})}),n(".woo-alipay-config-help .handlediv, .woo-alipay-config-help .handle").on("click",function(s){s.preventDefault(),n(".woo-alipay-config-help").toggleClass("closed")})});

View file

@ -1,9 +0,0 @@
<?php return array(
'dependencies' => array(
'wc-blocks-registry',
'wp-element',
'wp-html-entities',
'wp-i18n',
),
'version' => '3.1.0',
);

View file

@ -1,52 +0,0 @@
const { registerPaymentMethod } = window.wc.wcBlocksRegistry;
const { createElement } = window.wp.element;
const { __ } = window.wp.i18n;
const { decodeEntities } = window.wp.htmlEntities;

const settings = window.wc.wcSettings.getSetting( 'alipay_data', {} );
const defaultLabel = __( '支付宝', 'woo-alipay' );
const defaultDescription = __( '通过支付宝付款(中国大陆,包括香港和澳门)。', 'woo-alipay' );

const Label = ( props ) => {
const { PaymentMethodLabel } = props.components;
const iconElement = settings.icon ? createElement( 'img', {
src: settings.icon,
alt: decodeEntities( settings.title || defaultLabel ),
style: {
width: '24px',
height: '24px',
marginRight: '8px',
verticalAlign: 'middle'
}
} ) : null;
return createElement( 'div', {
style: { display: 'flex', alignItems: 'center' }
}, [
iconElement,
createElement( PaymentMethodLabel, {
text: decodeEntities( settings.title || defaultLabel ),
key: 'label'
} )
] );
};

const Content = () => {
return createElement( 'div', {
style: { padding: '10px 0' }
}, decodeEntities( settings.description || defaultDescription ) );
};

const alipayPaymentMethod = {
name: 'alipay',
label: createElement( Label ),
content: createElement( Content ),
edit: createElement( Content ),
canMakePayment: () => true,
ariaLabel: decodeEntities( settings.title || defaultLabel ),
supports: {
features: settings?.supports ?? ['products'],
},
};

registerPaymentMethod( alipayPaymentMethod );

43
lib/alipay/AopSdk.php Executable file
View file

@ -0,0 +1,43 @@
<?php
/**
* AOP SDK 入口文件
* 请不要修改这个文件,除非你知道怎样修改以及怎样恢复
* @author wuxiao
*/

/**
* 定义常量开始
* 在include("AopSdk.php")之前定义这些常量,不要直接修改本文件,以利于升级覆盖
*/
/**
* SDK工作目录
* 存放日志AOP缓存数据
*/
if (!defined("AOP_SDK_WORK_DIR"))
{
define("AOP_SDK_WORK_DIR", "/tmp/");
}
/**
* 是否处于开发模式
* 在你自己电脑上开发程序的时候千万不要设为false以免缓存造成你的代码修改了不生效
* 部署到生产环境正式运营后如果性能压力大可以把此常量设定为false能提高运行速度对应的代价就是你下次升级程序时要清一下缓存
*/
if (!defined("AOP_SDK_DEV_MODE"))
{
define("AOP_SDK_DEV_MODE", false);
}
/**
* 定义常量结束
*/

/**
* 找到lotusphp入口文件并初始化lotusphp
* lotusphp是一个第三方php框架其主页在lotusphp.googlecode.com
*/
$lotusHome = dirname(__FILE__) . DIRECTORY_SEPARATOR . "lotusphp_runtime" . DIRECTORY_SEPARATOR;
include($lotusHome . "Lotus.php");
$lotus = new Lotus;
$lotus->option["autoload_dir"] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'aop';
$lotus->devMode = AOP_SDK_DEV_MODE;
$lotus->defaultStoreDir = AOP_SDK_WORK_DIR;
$lotus->init();

1217
lib/alipay/aop/AopClient.php Executable file

File diff suppressed because it is too large Load diff

93
lib/alipay/aop/AopEncrypt.php Executable file
View file

@ -0,0 +1,93 @@
<?php
/**
* 加密工具类
*
* User: Alexandre Froger
* Date: 20/01/20
* Time: 下午9:06
*/

function AESEncrypt($message, $encodingAesKey = '', $appId = '') {
$key = base64_decode($encodingAesKey . '=');
$text = AESRandom(16) . pack("N", strlen($message)) . $message . $appid;
$iv = substr($key, 0, 16);
$blockSize = 32;
$textLength = strlen($text);
$amountToPad = $blockSize - ($textLength % $blockSize);
if (0 === $amountToPad) {
$amountToPad = $blockSize;
}

$padChr = chr($amountToPad);
$tmp = '';

for ($i = 0; $i < $amountToPad; $i++) {
$tmp .= $padChr;
}

$text = $text . $tmp;

$encrypted = openssl_encrypt(
$text,
'AES-256-CBC',
$key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);

$encryptMsg = base64_encode($encrypted);

return $encryptMsg;
}

function AESDecrypt($message, $encodingAesKey = '', $appId = '') {
$key = base64_decode($encodingAesKey);
$ciphertext = base64_decode($message);
$iv = substr($key, 0, 16);

$decrypted = openssl_decrypt(
$ciphertext,
'AES-256-CBC',
$key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);

$pad = ord(substr($decrypted, -1));

if ($pad < 1 || $pad > 32) {
$pad = 0;
}

$result = substr($decrypted, 0, (strlen($decrypted) - $pad));

if (strlen($result) < 16) {

return false;
}

$content = substr($result, 16);
$lenList = unpack("N", substr($content, 0, 4));
$len = $lenList[1];
$content = substr($content, 4, $len);
$fromAppId = substr($content, $len + 4);

if ($fromAppId != $appId) {

return false;
}

return $content;
}

function AESRandom($length = 16) {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";

for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}

return $str;
}

View file

@ -0,0 +1,19 @@
<?php
/**
* TODO 补充说明
*
* User: jiehua
* Date: 16/3/30
* Time: 下午8:55
*/

class EncryptParseItem {


public $startIndex;

public $endIndex;

public $encryptContent;

}

View file

@ -0,0 +1,18 @@
<?php
/**
* TODO 补充说明
*
* User: jiehua
* Date: 16/3/30
* Time: 下午8:51
*/

class EncryptResponseData {


public $realContent;

public $returnContent;


}

16
lib/alipay/aop/SignData.php Executable file
View file

@ -0,0 +1,16 @@
<?php
/**
* Created by PhpStorm.
* User: jiehua
* Date: 15/5/2
* Time: 下午6:21
*/

class SignData {

public $signSourceData=null;


public $sign=null;

}

View file

@ -0,0 +1,118 @@
<?php
/**
* ALIPAY API: alipay.data.dataservice.bill.downloadurl.query request
*
* @author auto create
* @since 1.0, 2016-09-20 16:35:20
*/
class AlipayDataDataserviceBillDownloadurlQueryRequest
{
/**
* 无授权模式的查询对账单下载地址
**/
private $bizContent;

private $apiParas = array();
private $terminalType;
private $terminalInfo;
private $prodCode;
private $apiVersion="1.0";
private $notifyUrl;
private $returnUrl;
private $needEncrypt=false;

public function setBizContent($bizContent)
{
$this->bizContent = $bizContent;
$this->apiParas["biz_content"] = $bizContent;
}

public function getBizContent()
{
return $this->bizContent;
}

public function getApiMethodName()
{
return "alipay.data.dataservice.bill.downloadurl.query";
}

public function setNotifyUrl($notifyUrl)
{
$this->notifyUrl=$notifyUrl;
}

public function getNotifyUrl()
{
return $this->notifyUrl;
}

public function setReturnUrl($returnUrl)
{
$this->returnUrl=$returnUrl;
}

public function getReturnUrl()
{
return $this->returnUrl;
}

public function getApiParas()
{
return $this->apiParas;
}

public function getTerminalType()
{
return $this->terminalType;
}

public function setTerminalType($terminalType)
{
$this->terminalType = $terminalType;
}

public function getTerminalInfo()
{
return $this->terminalInfo;
}

public function setTerminalInfo($terminalInfo)
{
$this->terminalInfo = $terminalInfo;
}

public function getProdCode()
{
return $this->prodCode;
}

public function setProdCode($prodCode)
{
$this->prodCode = $prodCode;
}

public function setApiVersion($apiVersion)
{
$this->apiVersion=$apiVersion;
}

public function getApiVersion()
{
return $this->apiVersion;
}

public function setNeedEncrypt($needEncrypt)
{

$this->needEncrypt=$needEncrypt;

}

public function getNeedEncrypt()
{
return $this->needEncrypt;
}

}

View file

@ -0,0 +1,118 @@
<?php
/**
* ALIPAY API: alipay.trade.close request
*
* @author auto create
* @since 1.0, 2016-11-09 22:08:22
*/
class AlipayTradeCloseRequest
{
/**
* 统一收单交易关闭接口
**/
private $bizContent;

private $apiParas = array();
private $terminalType;
private $terminalInfo;
private $prodCode;
private $apiVersion="1.0";
private $notifyUrl;
private $returnUrl;
private $needEncrypt=false;

public function setBizContent($bizContent)
{
$this->bizContent = $bizContent;
$this->apiParas["biz_content"] = $bizContent;
}

public function getBizContent()
{
return $this->bizContent;
}

public function getApiMethodName()
{
return "alipay.trade.close";
}

public function setNotifyUrl($notifyUrl)
{
$this->notifyUrl=$notifyUrl;
}

public function getNotifyUrl()
{
return $this->notifyUrl;
}

public function setReturnUrl($returnUrl)
{
$this->returnUrl=$returnUrl;
}

public function getReturnUrl()
{
return $this->returnUrl;
}

public function getApiParas()
{
return $this->apiParas;
}

public function getTerminalType()
{
return $this->terminalType;
}

public function setTerminalType($terminalType)
{
$this->terminalType = $terminalType;
}

public function getTerminalInfo()
{
return $this->terminalInfo;
}

public function setTerminalInfo($terminalInfo)
{
$this->terminalInfo = $terminalInfo;
}

public function getProdCode()
{
return $this->prodCode;
}

public function setProdCode($prodCode)
{
$this->prodCode = $prodCode;
}

public function setApiVersion($apiVersion)
{
$this->apiVersion=$apiVersion;
}

public function getApiVersion()
{
return $this->apiVersion;
}

public function setNeedEncrypt($needEncrypt)
{

$this->needEncrypt=$needEncrypt;

}

public function getNeedEncrypt()
{
return $this->needEncrypt;
}

}

View file

@ -0,0 +1,118 @@
<?php
/**
* ALIPAY API: alipay.trade.fastpay.refund.query request
*
* @author auto create
* @since 1.0, 2017-03-23 19:11:54
*/
class AlipayTradeFastpayRefundQueryRequest
{
/**
* 商户可使用该接口查询自已通过alipay.trade.refund提交的退款请求是否执行成功。
**/
private $bizContent;

private $apiParas = array();
private $terminalType;
private $terminalInfo;
private $prodCode;
private $apiVersion="1.0";
private $notifyUrl;
private $returnUrl;
private $needEncrypt=false;

public function setBizContent($bizContent)
{
$this->bizContent = $bizContent;
$this->apiParas["biz_content"] = $bizContent;
}

public function getBizContent()
{
return $this->bizContent;
}

public function getApiMethodName()
{
return "alipay.trade.fastpay.refund.query";
}

public function setNotifyUrl($notifyUrl)
{
$this->notifyUrl=$notifyUrl;
}

public function getNotifyUrl()
{
return $this->notifyUrl;
}

public function setReturnUrl($returnUrl)
{
$this->returnUrl=$returnUrl;
}

public function getReturnUrl()
{
return $this->returnUrl;
}

public function getApiParas()
{
return $this->apiParas;
}

public function getTerminalType()
{
return $this->terminalType;
}

public function setTerminalType($terminalType)
{
$this->terminalType = $terminalType;
}

public function getTerminalInfo()
{
return $this->terminalInfo;
}

public function setTerminalInfo($terminalInfo)
{
$this->terminalInfo = $terminalInfo;
}

public function getProdCode()
{
return $this->prodCode;
}

public function setProdCode($prodCode)
{
$this->prodCode = $prodCode;
}

public function setApiVersion($apiVersion)
{
$this->apiVersion=$apiVersion;
}

public function getApiVersion()
{
return $this->apiVersion;
}

public function setNeedEncrypt($needEncrypt)
{

$this->needEncrypt=$needEncrypt;

}

public function getNeedEncrypt()
{
return $this->needEncrypt;
}

}

View file

@ -0,0 +1,118 @@
<?php
/**
* ALIPAY API: alipay.trade.page.pay request
*
* @author auto create
* @since 1.0, 2017-04-06 15:55:36
*/
class AlipayTradePagePayRequest
{
/**
* 统一收单下单并支付页面接口
**/
private $bizContent;

private $apiParas = array();
private $terminalType;
private $terminalInfo;
private $prodCode;
private $apiVersion="1.0";
private $notifyUrl;
private $returnUrl;
private $needEncrypt=false;

public function setBizContent($bizContent)
{
$this->bizContent = $bizContent;
$this->apiParas["biz_content"] = $bizContent;
}

public function getBizContent()
{
return $this->bizContent;
}

public function getApiMethodName()
{
return "alipay.trade.page.pay";
}

public function setNotifyUrl($notifyUrl)
{
$this->notifyUrl=$notifyUrl;
}

public function getNotifyUrl()
{
return $this->notifyUrl;
}

public function setReturnUrl($returnUrl)
{
$this->returnUrl=$returnUrl;
}

public function getReturnUrl()
{
return $this->returnUrl;
}

public function getApiParas()
{
return $this->apiParas;
}

public function getTerminalType()
{
return $this->terminalType;
}

public function setTerminalType($terminalType)
{
$this->terminalType = $terminalType;
}

public function getTerminalInfo()
{
return $this->terminalInfo;
}

public function setTerminalInfo($terminalInfo)
{
$this->terminalInfo = $terminalInfo;
}

public function getProdCode()
{
return $this->prodCode;
}

public function setProdCode($prodCode)
{
$this->prodCode = $prodCode;
}

public function setApiVersion($apiVersion)
{
$this->apiVersion=$apiVersion;
}

public function getApiVersion()
{
return $this->apiVersion;
}

public function setNeedEncrypt($needEncrypt)
{

$this->needEncrypt=$needEncrypt;

}

public function getNeedEncrypt()
{
return $this->needEncrypt;
}

}

View file

@ -0,0 +1,119 @@
<?php
/**
* ALIPAY API: alipay.trade.query request
*
* @author auto create
* @since 1.0, 2017-01-09 15:37:43
*/
class AlipayTradeQueryRequest
{
/**
* 统一收单线下交易查询
修改路由策略到R
**/
private $bizContent;

private $apiParas = array();
private $terminalType;
private $terminalInfo;
private $prodCode;
private $apiVersion="1.0";
private $notifyUrl;
private $returnUrl;
private $needEncrypt=false;

public function setBizContent($bizContent)
{
$this->bizContent = $bizContent;
$this->apiParas["biz_content"] = $bizContent;
}

public function getBizContent()
{
return $this->bizContent;
}

public function getApiMethodName()
{
return "alipay.trade.query";
}

public function setNotifyUrl($notifyUrl)
{
$this->notifyUrl=$notifyUrl;
}

public function getNotifyUrl()
{
return $this->notifyUrl;
}

public function setReturnUrl($returnUrl)
{
$this->returnUrl=$returnUrl;
}

public function getReturnUrl()
{
return $this->returnUrl;
}

public function getApiParas()
{
return $this->apiParas;
}

public function getTerminalType()
{
return $this->terminalType;
}

public function setTerminalType($terminalType)
{
$this->terminalType = $terminalType;
}

public function getTerminalInfo()
{
return $this->terminalInfo;
}

public function setTerminalInfo($terminalInfo)
{
$this->terminalInfo = $terminalInfo;
}

public function getProdCode()
{
return $this->prodCode;
}

public function setProdCode($prodCode)
{
$this->prodCode = $prodCode;
}

public function setApiVersion($apiVersion)
{
$this->apiVersion=$apiVersion;
}

public function getApiVersion()
{
return $this->apiVersion;
}

public function setNeedEncrypt($needEncrypt)
{

$this->needEncrypt=$needEncrypt;

}

public function getNeedEncrypt()
{
return $this->needEncrypt;
}

}

View file

@ -0,0 +1,118 @@
<?php
/**
* ALIPAY API: alipay.trade.refund request
*
* @author auto create
* @since 1.0, 2017-01-13 19:12:23
*/
class AlipayTradeRefundRequest
{
/**
* 统一收单交易退款接口
**/
private $bizContent;

private $apiParas = array();
private $terminalType;
private $terminalInfo;
private $prodCode;
private $apiVersion="1.0";
private $notifyUrl;
private $returnUrl;
private $needEncrypt=false;

public function setBizContent($bizContent)
{
$this->bizContent = $bizContent;
$this->apiParas["biz_content"] = $bizContent;
}

public function getBizContent()
{
return $this->bizContent;
}

public function getApiMethodName()
{
return "alipay.trade.refund";
}

public function setNotifyUrl($notifyUrl)
{
$this->notifyUrl=$notifyUrl;
}

public function getNotifyUrl()
{
return $this->notifyUrl;
}

public function setReturnUrl($returnUrl)
{
$this->returnUrl=$returnUrl;
}

public function getReturnUrl()
{
return $this->returnUrl;
}

public function getApiParas()
{
return $this->apiParas;
}

public function getTerminalType()
{
return $this->terminalType;
}

public function setTerminalType($terminalType)
{
$this->terminalType = $terminalType;
}

public function getTerminalInfo()
{
return $this->terminalInfo;
}

public function setTerminalInfo($terminalInfo)
{
$this->terminalInfo = $terminalInfo;
}

public function getProdCode()
{
return $this->prodCode;
}

public function setProdCode($prodCode)
{
$this->prodCode = $prodCode;
}

public function setApiVersion($apiVersion)
{
$this->apiVersion=$apiVersion;
}

public function getApiVersion()
{
return $this->apiVersion;
}

public function setNeedEncrypt($needEncrypt)
{

$this->needEncrypt=$needEncrypt;

}

public function getNeedEncrypt()
{
return $this->needEncrypt;
}

}

View file

@ -0,0 +1,118 @@
<?php
/**
* ALIPAY API: alipay.trade.wap.pay request
*
* @author auto create
* @since 1.0, 2017-04-05 17:31:42
*/
class AlipayTradeWapPayRequest
{
/**
* 手机网站支付接口2.0
**/
private $bizContent;

private $apiParas = array();
private $terminalType;
private $terminalInfo;
private $prodCode;
private $apiVersion="1.0";
private $notifyUrl;
private $returnUrl;
private $needEncrypt=false;

public function setBizContent($bizContent)
{
$this->bizContent = $bizContent;
$this->apiParas["biz_content"] = $bizContent;
}

public function getBizContent()
{
return $this->bizContent;
}

public function getApiMethodName()
{
return "alipay.trade.wap.pay";
}

public function setNotifyUrl($notifyUrl)
{
$this->notifyUrl=$notifyUrl;
}

public function getNotifyUrl()
{
return $this->notifyUrl;
}

public function setReturnUrl($returnUrl)
{
$this->returnUrl=$returnUrl;
}

public function getReturnUrl()
{
return $this->returnUrl;
}

public function getApiParas()
{
return $this->apiParas;
}

public function getTerminalType()
{
return $this->terminalType;
}

public function setTerminalType($terminalType)
{
$this->terminalType = $terminalType;
}

public function getTerminalInfo()
{
return $this->terminalInfo;
}

public function setTerminalInfo($terminalInfo)
{
$this->terminalInfo = $terminalInfo;
}

public function getProdCode()
{
return $this->prodCode;
}

public function setProdCode($prodCode)
{
$this->prodCode = $prodCode;
}

public function setApiVersion($apiVersion)
{
$this->apiVersion=$apiVersion;
}

public function getApiVersion()
{
return $this->apiVersion;
}

public function setNeedEncrypt($needEncrypt)
{

$this->needEncrypt=$needEncrypt;

}

public function getNeedEncrypt()
{
return $this->needEncrypt;
}

}

View file

@ -0,0 +1,306 @@
<?php
class LtAutoloader
{
public $conf = array(
/**
* 是否自动加载定义了函数的文件
*
* 可选项:
* # true 自动加载
* # false 跳过函数只自动加载定义了class或者interface的文件
*/
"load_function" => true,

/**
* 要扫描的文件类型
*
* 若该属性设置为array("php","inc","php3")
* 则扩展名为"php","inc","php3"的文件会被扫描,
* 其它扩展名的文件会被忽略
*/
"allow_file_extension" => array("php", "inc"),

/**
* 不扫描的目录
*
* 若该属性设置为array(".svn", ".setting")
* 则所有名为".setting"的目录也会被忽略
*/
"skip_dir_names" => array(".svn"),
);

public $storeHandle;
public $autoloadPath;
protected $functionFileMapping;
protected $fileStore;

public function init()
{
if (!is_object($this->storeHandle))
{
$this->storeHandle = new LtStoreMemory;
$this->fileStore = new LtStoreFile;
$this->fileStore->prefix = 'LtAutoloader-token-cache';
$this->fileStore->useSerialize = true;
$this->fileStore->init();
}
// Whether scanning directory
if (0 == $this->storeHandle->get(".class_total") && 0 == $this->storeHandle->get(".function_total"))
{
$this->storeHandle->add(".class_total", 0);
$this->storeHandle->add(".function_total", 0);
$this->storeHandle->add(".functions", array(), 0);
$autoloadPath = $this->preparePath($this->autoloadPath);
foreach($autoloadPath as $key => $path)
{
if (is_file($path))
{
$this->addFileMap($path);
unset($autoloadPath[$key]);
}
}
$this->scanDirs($autoloadPath);
unset($autoloadPath);
}
// Whether loading function files
if ($this->conf["load_function"])
{
$this->loadFunction();
}
spl_autoload_register(array($this, "loadClass"));
}

public function loadFunction()
{
if ($functionFiles = $this->storeHandle->get(".functions"))
{
foreach ($functionFiles as $functionFile)
{
include($functionFile);
}
}
}

public function loadClass($className)
{
if ($classFile = $this->storeHandle->get(strtolower($className)))
{
include($classFile);
}
}

protected function convertPath($path)
{
$path = str_replace("\\", "/", $path);
if (!is_readable($path))
{
trigger_error("Directory is not exists/readable: {$path}");
return false;
}
$path = rtrim(realpath($path), '\\/');
if ("WINNT" != PHP_OS && preg_match("/\s/i", $path))
{
trigger_error("Directory contains space/tab/newline is not supported: {$path}");
return false;
}
return $path;
}

/**
* The string or an Multidimensional array into a one-dimensional array
*
* @param array $ or string $var
* @return array one-dimensional array
*/
protected function preparePath($var)
{
$ret = array();
if (!is_array($var))
{
$var = array($var);
}
$i = 0;
while (isset($var[$i]))
{
if (!is_array($var[$i]) && $path = $this->convertPath($var[$i]))
{
$ret[] = $path;
}
else
{
foreach($var[$i] as $v)
{
$var[] = $v;
}
}
unset($var[$i]);
$i ++;
}
return $ret;
}

/**
* Using iterative algorithm scanning subdirectories
* save autoloader filemap
*
* @param array $dirs one-dimensional
* @return
*/
protected function scanDirs($dirs)
{
$i = 0;
while (isset($dirs[$i]))
{
$dir = $dirs[$i];
$files = scandir($dir);
foreach ($files as $file)
{
if (in_array($file, array(".", "..")) || in_array($file, $this->conf["skip_dir_names"]))
{
continue;
}
$currentFile = $dir . DIRECTORY_SEPARATOR . $file;
if (is_file($currentFile))
{
$this->addFileMap($currentFile);
}
else if (is_dir($currentFile))
{
// if $currentFile is a directory, pass through the next loop.
$dirs[] = $currentFile;
}
else
{
trigger_error("$currentFile is not a file or a directory.");
}
} //end foreach
unset($dirs[$i]);
$i ++;
} //end while
}

protected function parseLibNames($src)
{
$libNames = array();
$tokens = token_get_all($src);
$level = 0;
$found = false;
$name = '';
foreach ($tokens as $token)
{
if (is_string($token))
{
if ('{' == $token)
{
$level ++;
}
else if ('}' == $token)
{
$level --;
}
}
else
{
list($id, $text) = $token;
if (T_CURLY_OPEN == $id || T_DOLLAR_OPEN_CURLY_BRACES == $id)
{
$level ++;
}
if (0 < $level)
{
continue;
}
switch ($id)
{
case T_STRING:
if ($found)
{
$libNames[strtolower($name)][] = $text;
$found = false;
}
break;
case T_CLASS:
case T_INTERFACE:
case T_FUNCTION:
$found = true;
$name = $text;
break;
}
}
}
return $libNames;
}

protected function addClass($className, $file)
{
$key = strtolower($className);
if ($existedClassFile = $this->storeHandle->get($key))
{
trigger_error("duplicate class [$className] found in:\n$existedClassFile\n$file\n");
return false;
}
else
{
$this->storeHandle->add($key, $file);
$this->storeHandle->update(".class_total", $this->storeHandle->get(".class_total") + 1);
return true;
}
}

protected function addFunction($functionName, $file)
{
$functionName = strtolower($functionName);
if (isset($this->functionFileMapping[$functionName]))
{
$existedFunctionFile = $this->functionFileMapping[$functionName];
trigger_error("duplicate function [$functionName] found in:\n$existedFunctionFile\n$file\n");
return false;
}
else
{
$this->functionFileMapping[$functionName] = $file;
$this->storeHandle->update(".functions", array_unique(array_values($this->functionFileMapping)));
$this->storeHandle->update(".function_total", count($this->functionFileMapping));
return true;
}
}

protected function addFileMap($file)
{
if (!in_array(pathinfo($file, PATHINFO_EXTENSION), $this->conf["allow_file_extension"]))
{
return false;
}
$libNames = array();
if ($this->fileStore instanceof LtStore)
{
$cachedFileLastModified = (int) @filemtime($this->fileStore->getFilePath($file));
if ($cachedFileLastModified < filemtime($file) || !is_array(($libNames = $this->fileStore->get($file))))
{
$libNames = $this->parseLibNames(trim(file_get_contents($file)));
if (0 < $cachedFileLastModified)
{
$this->fileStore->update($file, $libNames);
}
else
{
$this->fileStore->add($file, $libNames);
}
}
}
else
{
$libNames = $this->parseLibNames(trim(file_get_contents($file)));
}

foreach ($libNames as $libType => $libArray)
{
$method = "function" == $libType ? "addFunction" : "addClass";
foreach ($libArray as $libName)
{
$this->$method($libName, $file);
}
}
return true;
}
}

View file

@ -0,0 +1,9 @@
<?php
interface LtCacheAdapter
{
public function connect($hostConf);
public function add($key, $value, $ttl = 0, $tableName, $connectionResource);
public function del($key, $tableName, $connectionResource);
public function get($key, $tableName, $connectionResource);
public function update($key, $value, $ttl = 0, $tableName, $connectionResource);
}

View file

@ -0,0 +1,40 @@
<?php
class LtCacheAdapterApc implements LtCacheAdapter
{
public function connect($hostConf)
{
return true;
}

public function add($key, $value, $ttl = 0, $tableName, $connectionResource)
{
return apc_add($this->getRealKey($tableName, $key), $value, $ttl);
}

public function del($key, $tableName, $connectionResource)
{
return apc_delete($this->getRealKey($tableName, $key));
}

public function get($key, $tableName, $connectionResource)
{
return apc_fetch($this->getRealKey($tableName, $key));
}

public function update($key, $value, $ttl = 0, $tableName, $connectionResource)
{
if ($this->del($key, $tableName, $connectionResource))
{
return $this->add($key, $value, $ttl, $tableName, $connectionResource);
}
else
{
return false;
}
}

protected function getRealKey($tableName, $key)
{
return $tableName . "-" . $key;
}
}

View file

@ -0,0 +1,49 @@
<?php
class LtCacheAdapterEAccelerator implements LtCacheAdapter
{
public function connect($hostConf)
{
return true;
}

public function add($key, $value, $ttl=0, $tableName, $connectionResource)
{
$value = serialize($value); //eAccelerator doesn't serialize object
return eaccelerator_put($this->getRealKey($tableName, $key), $value, $ttl);
}

public function del($key, $tableName, $connectionResource)
{
return eaccelerator_rm($this->getRealKey($tableName, $key));
}

public function get($key, $tableName, $connectionResource)
{
$value = eaccelerator_get($this->getRealKey($tableName, $key));
if (!empty($value))
{
return unserialize($value);
}
else
{
return false;
}
}

public function update($key, $value, $ttl = 0, $tableName, $connectionResource)
{
if ($this->del($key, $tableName, $connectionResource))
{
return $this->add($key, $value, $ttl, $tableName, $connectionResource);
}
else
{
return false;
}
}

protected function getRealKey($tableName, $key)
{
return $tableName . "-" . $key;
}
}

View file

@ -0,0 +1,68 @@
<?php
class LtCacheAdapterFile implements LtCacheAdapter
{
public function connect($hostConf)
{
$fileStore = new LtStoreFile;
$fileStore->prefix = 'LtCache-file';
$fileStore->useSerialize = true;
$fileStore->init();
return $fileStore;
}

public function add($key, $value, $ttl = 0, $tableName, $connectionResource)
{
if (0 != $ttl)
{
$ttl += time();
}
if (true == $connectionResource->add($this->getRealKey($tableName, $key), array("ttl" => $ttl, "value" => $value)))
{
return true;
}
else
{
if ($this->get($key,$tableName,$connectionResource))
{
return false;
}
else
{
$this->del($key,$tableName,$connectionResource);
return $connectionResource->add($this->getRealKey($tableName, $key), array("ttl" => $ttl, "value" => $value));
}
}
}

public function del($key, $tableName, $connectionResource)
{
return $connectionResource->del($this->getRealKey($tableName, $key));
}

public function get($key, $tableName, $connectionResource)
{
$cachedArray = $connectionResource->get($this->getRealKey($tableName, $key));
if (is_array($cachedArray) && (0 == $cachedArray["ttl"] || $cachedArray["ttl"] > time()))
{
return $cachedArray["value"];
}
else
{
return false;
}
}

public function update($key, $value, $ttl = 0, $tableName, $connectionResource)
{
if (0 != $ttl)
{
$ttl += time();
}
return $connectionResource->update($this->getRealKey($tableName, $key), array("ttl" => $ttl, "value" => $value));
}

protected function getRealKey($tableName, $key)
{
return $tableName . "-" . $key;
}
}

View file

@ -0,0 +1,33 @@
<?php
class LtCacheAdapterMemcache implements LtCacheAdapter
{
public function connect($hostConf)
{
return memcache_connect($hostConf["host"], $hostConf["port"]);
}

public function add($key, $value, $ttl = 0, $tableName, $connectionResource)
{
return $connectionResource->add($this->getRealKey($tableName, $key), $value, false, $ttl);
}

public function del($key, $tableName, $connectionResource)
{
return $connectionResource->delete($this->getRealKey($tableName, $key), 0);
}

public function get($key, $tableName, $connectionResource)
{
return $connectionResource->get($this->getRealKey($tableName, $key));
}

public function update($key, $value, $ttl = 0, $tableName, $connectionResource)
{
return $connectionResource->replace($this->getRealKey($tableName, $key), $value, false, $ttl);
}

protected function getRealKey($tableName, $key)
{
return $tableName . "-" . $key;
}
}

View file

@ -0,0 +1,35 @@
<?php
class LtCacheAdapterMemcached implements LtCacheAdapter
{
public function connect($hostConf)
{
$connectionResource = new Memcached();
$connectionResource->addServer($hostConf["host"], $hostConf["port"]);
return $connectionResource;
}

public function add($key, $value, $ttl=0, $tableName, $connectionResource)
{
return $connectionResource->add($this->getRealKey($tableName, $key), $value, $ttl);
}

public function del($key, $tableName, $connectionResource)
{
return $connectionResource->delete($this->getRealKey($tableName, $key));
}

public function get($key, $tableName, $connectionResource)
{
return $connectionResource->get($this->getRealKey($tableName, $key));
}

public function update($key, $value, $ttl = 0, $tableName, $connectionResource)
{
return $connectionResource->replace($this->getRealKey($tableName, $key), $value, $ttl);
}

protected function getRealKey($tableName, $key)
{
return $tableName . "-" . $key;
}
}

View file

@ -0,0 +1,55 @@
<?php
class LtCacheAdapterPhps implements LtCacheAdapter
{
public function connect($hostConf)
{
$fileStore = new LtStoreFile;
if (isset($hostConf['host']) && is_string($hostConf['host']))
{
$fileStore->cacheFileRoot = $hostConf['host'];
$fileStore->prefix = 'Ltcache-phps-';
$fileStore->init();
return $fileStore;
}
else
{
trigger_error("Must set [host]");
return false;
}
}

public function add($key, $value, $ttl = 0, $tableName, $connectionResource)
{
return $connectionResource->add($this->getRealKey($tableName, $key), $this->valueToString($value), $ttl);
}

public function del($key, $tableName, $connectionResource)
{
return $connectionResource->del($this->getRealKey($tableName, $key));
}

public function get($key, $tableName, $connectionResource)
{
return $this->stringToValue($connectionResource->get($this->getRealKey($tableName, $key)));
}

public function update($key, $value, $ttl = 0, $tableName, $connectionResource)
{
return $connectionResource->update($this->getRealKey($tableName, $key), $this->valueToString($value), $ttl);
}

protected function getRealKey($tableName, $key)
{
return $tableName . "-" . $key;
}

protected function valueToString($value)
{
return serialize($value);
}

protected function stringToValue($str)
{
return unserialize($str);
}
}

View file

@ -0,0 +1,43 @@
<?php
class LtCacheAdapterXcache implements LtCacheAdapter
{
public function connect($hostConf)
{
return true;
}

public function add($key, $value, $ttl = 0, $tableName, $connectionResource)
{
return xcache_set($this->getRealKey($tableName, $key), $value, $ttl);
}

public function del($key, $tableName, $connectionResource)
{
return xcache_unset($this->getRealKey($tableName, $key));
}

public function get($key, $tableName, $connectionResource)
{
$key = $this->getRealKey($tableName, $key);
if (xcache_isset($key))
{
return xcache_get($key);
}
return false;
}

public function update($key, $value, $ttl = 0, $tableName, $connectionResource)
{
$key = $this->getRealKey($tableName, $key);
if (xcache_isset($key))
{
return xcache_set($key, $value, $ttl);
}
return false;
}

protected function getRealKey($tableName, $key)
{
return $tableName . "-" . $key;
}
}

View file

@ -0,0 +1,76 @@
<?php
class LtCache
{
public $configHandle;

public $group;
public $node;

protected $ch;

public function __construct()
{
if (! $this->configHandle instanceof LtConfig)
{
if (class_exists("LtObjectUtil", false))
{
$this->configHandle = LtObjectUtil::singleton("LtConfig");
}
else
{
$this->configHandle = new LtConfig;
}
}
}

public function init()
{
$this->ch = new LtCacheHandle;
$this->ch->configHandle = $this->configHandle;
$this->ch->init();
$this->ch->group = $this->getGroup();
$this->ch->node = $this->getNode();
}

public function getTDG($tableName)
{
$tdg = new LtCacheTableDataGateway;
$tdg->tableName = $tableName;
$tdg->ch = $this->ch;
return $tdg;
}

public function changeNode($node)
{
$this->node = $node;
$this->dbh->node = $node;
}

protected function getGroup()
{
if ($this->group)
{
return $this->group;
}
$servers = $this->configHandle->get("cache.servers");
if (1 == count($servers))
{
return key($servers);
}
return false;
}

protected function getNode()
{
if ($this->node)
{
return $this->node;
}
$servers = $this->configHandle->get("cache.servers");
if (1 == count($servers[$this->getGroup()]))
{
return key($servers[$this->getGroup()]);
}
return false;
}
}

View file

@ -0,0 +1,14 @@
<?php
class LtCacheAdapterFactory
{
public function getConnectionAdapter($adapter)
{
$adapterClassName = "LtCacheAdapter" . ucfirst($adapter);
if(!class_exists($adapterClassName))
{
trigger_error("Invalid adapter: $adapter");
return null;
}
return new $adapterClassName;
}
}

View file

@ -0,0 +1,49 @@
<?php
class LtCacheConfigBuilder
{
protected $servers = array();

protected $defaultConfig = array(
"adapter" => "phps", //apc,xcach,ea; file, phps; memcached
//"prefix" => ""
//"host" => "localhost", //some ip, hostname
//"port" => 3306,
);

public function addSingleHost($hostConfig)
{
$this->addHost("group_0", "node_0", "master", $hostConfig);
}

public function addHost($groupId, $nodeId = "node_0", $role = "master", $hostConfig)
{
if (isset($this->servers[$groupId][$nodeId][$role]))
{//以相同role的第一个host为默认配置
$ref = $this->servers[$groupId][$nodeId][$role][0];
}
else if ("slave" == $role && isset($this->servers[$groupId][$nodeId]["master"]))
{//slave host以master的第一个host为默认配置
$ref = $this->servers[$groupId][$nodeId]["master"][0];
}
else if (isset($this->servers[$groupId]) && count($this->servers[$groupId]))
{//以本group第一个node的master第一个host为默认配置
$refNode = key($this->servers[$groupId]);
$ref = $this->servers[$groupId][$refNode]["master"][0];
}
else
{
if (!isset($hostConfig["adapter"]))
{
trigger_error("No db adapter specified");
}
$ref = $this->defaultConfig;
}
$conf = array_merge($ref, $hostConfig);
$this->servers[$groupId][$nodeId][$role][] = $conf;
}

public function getServers()
{
return $this->servers;
}
}

View file

@ -0,0 +1,52 @@
<?php
class LtCacheConnectionManager
{
public $configHandle;
protected $connectionAdapter;

public function getConnection($group, $node, $role)
{
if ($connection = $this->getNewConnection($group, $node, $role))
{
return array(
"connectionAdapter" => $this->connectionAdapter,
"connectionResource" => $connection
);
}
else
{
trigger_error("no cache server can be connected");
return false;
}
}

protected function getNewConnection($group, $node, $role)
{
$servers = $this->configHandle->get("cache.servers");
$hostTotal = count($servers[$group][$node][$role]);
$hostIndexArray = array_keys($servers[$group][$node][$role]);
while ($hostTotal)
{
$hashNumber = substr(microtime(),7,1) % $hostTotal;
$hostConfig = $servers[$group][$node][$role][$hostIndexArray[$hashNumber]];
$cacheFactory = new LtCacheAdapterFactory;
$this->connectionAdapter = $cacheFactory->getConnectionAdapter($hostConfig["adapter"]);
if ($connection = $this->connectionAdapter->connect($hostConfig))
{
return $connection;
}
else
{
//trigger_error('connection fail', E_USER_WARNING);
//delete the unavailable server
for ($i = $hashNumber; $i < $hostTotal - 1; $i ++)
{
$hostIndexArray[$i] = $hostIndexArray[$i+1];
}
unset($hostIndexArray[$hostTotal-1]);
$hostTotal --;
}//end else
}//end while
return false;
}
}

View file

@ -0,0 +1,52 @@
<?php
class LtCacheHandle
{
public $configHandle;
public $group;
public $node;
public $role = "master";
public $connectionManager;
public $connectionResource;
protected $connectionAdapter;

public function __construct()
{
}

public function init()
{
$this->connectionManager = new LtCacheConnectionManager;
$this->connectionManager->configHandle =$this->configHandle;
}

public function add($key, $value, $ttl = 0, $tableName)
{
$this->initConnection();
return $this->connectionAdapter->add($key, $value, $ttl, $tableName, $this->connectionResource);
}

public function del($key, $tableName)
{
$this->initConnection();
return $this->connectionAdapter->del($key, $tableName, $this->connectionResource);
}

public function get($key, $tableName)
{
$this->initConnection();
return $this->connectionAdapter->get($key, $tableName, $this->connectionResource);
}

public function update($key, $value, $ttl = 0, $tableName)
{
$this->initConnection();
return $this->connectionAdapter->update($key, $value, $ttl, $tableName, $this->connectionResource);
}

protected function initConnection()
{
$connectionInfo = $this->connectionManager->getConnection($this->group, $this->node, $this->role);
$this->connectionAdapter = $connectionInfo["connectionAdapter"];
$this->connectionResource = $connectionInfo["connectionResource"];
}
}

View file

@ -0,0 +1,27 @@
<?php
class LtCacheTableDataGateway
{
public $tableName;

public $ch;

public function add($key, $value, $ttl = 0)
{
return $this->ch->add($key, $value, $ttl, $this->tableName);
}

public function del($key)
{
return $this->ch->del($key, $this->tableName);
}

public function get($key)
{
return $this->ch->get($key, $this->tableName);
}

public function update($key, $value, $ttl = 0)
{
return $this->ch->update($key, $value, $ttl, $this->tableName);
}
}

View file

@ -0,0 +1,91 @@
<?php
class LtCaptcha
{
public $configHandle;
public $storeHandle;

public $imageEngine;

public function __construct()
{
if (! $this->configHandle instanceof LtConfig)
{
if (class_exists("LtObjectUtil", false))
{
$this->configHandle = LtObjectUtil::singleton("LtConfig");
}
else
{
$this->configHandle = new LtConfig;
}
}
}

public function init()
{
if (!is_object($this->storeHandle))
{
$this->storeHandle = new LtStoreFile;
$this->storeHandle->prefix = 'LtCaptcha-seed-';
$this->storeHandle->init();
}
}

public function getImageResource($seed)
{
if (empty($seed))
{
trigger_error("empty seed");
return false;
}
if (!is_object($this->imageEngine))
{
if ($imageEngine = $this->configHandle->get("captcha.image_engine"))
{
if (class_exists($imageEngine))
{
$this->imageEngine = new $imageEngine;
$this->imageEngine->conf = $this->configHandle->get("captcha.image_engine_conf");
}
else
{
trigger_error("captcha.image_engine : $imageEngine not exists");
}
}
else
{
trigger_error("empty captcha.image_engine");
return false;
}
}
$word = $this->generateRandCaptchaWord($seed);
$this->storeHandle->add($seed, $word);
return $this->imageEngine->drawImage($word);
}

public function verify($seed, $userInput)
{
if ($word = $this->storeHandle->get($seed))
{
$this->storeHandle->del($seed);
return $userInput === $word;
}
else
{
return false;
}
}

protected function generateRandCaptchaWord()
{
$allowChars = $this->configHandle->get("captcha.allow_chars");
$length = $this->configHandle->get("captcha.length");
$allowedSymbolsLength = strlen($allowChars) - 1;
$captchaWord = "";
for ($i = 0; $i < $length; $i ++)
{
$captchaWord .= $allowChars[mt_rand(0, $allowedSymbolsLength)];
}
return $captchaWord;
}
}

View file

@ -0,0 +1,197 @@
<?php
/**
* Lotus Captcha component is inspired by "cool-php-captcha" project
* http://code.google.com/p/cool-php-captcha
*/
class LtCaptchaImageEngine
{
public $conf;

protected $maxWordLength = 9;

/**
* * Background color in RGB-array
*/
protected $backgroundColor = array(255, 255, 255);

/**
* * Foreground colors in RGB-array
*/
protected $colors = array(
array(27, 78, 181), // blue
array(22, 163, 35), // green
array(214, 36, 7), // red
);

/**
* * Shadow color in RGB-array or null
*/
protected $shadowColor = null; //array(0, 0, 0);
/**
* Font configuration
*
* - font: TTF file
* - spacing: relative pixel space between character
* - minSize: min font size
* - maxSize: max font size
*/
protected $fonts = array('Antykwa' => array('spacing' => -3, 'minSize' => 27, 'maxSize' => 30, 'font' => 'AntykwaBold.ttf'),
'Candice' => array('spacing' => -1.5, 'minSize' => 28, 'maxSize' => 31, 'font' => 'Candice.ttf'),
'DingDong' => array('spacing' => -2, 'minSize' => 24, 'maxSize' => 30, 'font' => 'Ding-DongDaddyO.ttf'),
'Duality' => array('spacing' => -2, 'minSize' => 30, 'maxSize' => 38, 'font' => 'Duality.ttf'),
'Jura' => array('spacing' => -2, 'minSize' => 28, 'maxSize' => 32, 'font' => 'Jura.ttf'),
'StayPuft' => array('spacing' => -1.5, 'minSize' => 28, 'maxSize' => 32, 'font' => 'StayPuft.ttf'),
'Times' => array('spacing' => -2, 'minSize' => 28, 'maxSize' => 34, 'font' => 'TimesNewRomanBold.ttf'),
'VeraSans' => array('spacing' => -1, 'minSize' => 20, 'maxSize' => 28, 'font' => 'VeraSansBold.ttf'),
);

/**
* * Wave configuracion in X and Y axes
*/
protected $Yperiod = 12;
protected $Yamplitude = 14;
protected $Xperiod = 11;
protected $Xamplitude = 5;

/**
* * GD image
*/
protected $im;

public function drawImage($text)
{
/**
* * Initialization
*/
$this->ImageAllocate();

$fontcfg = $this->fonts[array_rand($this->fonts)];
$this->WriteText($text, $fontcfg);

/**
* * Transformations
*/
$this->WaveImage();
if ($this->conf['blur'] && function_exists('imagefilter'))
{
imagefilter($this->im, IMG_FILTER_GAUSSIAN_BLUR);
}
$this->ReduceImage();
return $this->im;
}

/**
* Creates the image resources
*/
protected function ImageAllocate()
{
// Cleanup
if (!empty($this->im))
{
imagedestroy($this->im);
}

$this->im = imagecreatetruecolor($this->conf['width'] * $this->conf['scale'], $this->conf['height'] * $this->conf['scale']);
// Background color
$this->GdBgColor = imagecolorallocate($this->im,
$this->backgroundColor[0],
$this->backgroundColor[1],
$this->backgroundColor[2]
);
imagefilledrectangle($this->im, 0, 0, $this->conf['width'] * $this->conf['scale'], $this->conf['height'] * $this->conf['scale'], $this->GdBgColor);
// Foreground color
$color = $this->colors[mt_rand(0, sizeof($this->colors)-1)];
$this->GdFgColor = imagecolorallocate($this->im, $color[0], $color[1], $color[2]);
// Shadow color
if (!empty($this->shadowColor) && is_array($this->shadowColor) && sizeof($this->shadowColor) >= 3)
{
$this->GdShadowColor = imagecolorallocate($this->im,
$this->shadowColor[0],
$this->shadowColor[1],
$this->shadowColor[2]
);
}
}

/**
* Text insertion
*/
protected function WriteText($text, $fontcfg = array())
{
if (empty($fontcfg))
{
// Select the font configuration
$fontcfg = $this->fonts[array_rand($this->fonts)];
}
// Full path of font file
$fontfile = dirname(__FILE__) . '/fonts/' . $fontcfg['font'];

/**
* * Increase font-size for shortest words: 9% for each glyp missing
*/
$lettersMissing = $this->maxWordLength - strlen($text);
$fontSizefactor = 1 + ($lettersMissing * 0.09);
// Text generation (char by char)
$x = 20 * $this->conf['scale'];
$y = round(($this->conf['height'] * 27 / 40) * $this->conf['scale']);
$length = strlen($text);
for ($i = 0; $i < $length; $i++)
{
$degree = rand($this->conf['max_rotation'] * -1, $this->conf['max_rotation']);
$fontsize = rand($fontcfg['minSize'], $fontcfg['maxSize']) * $this->conf['scale'] * $fontSizefactor;
$letter = substr($text, $i, 1);

if ($this->shadowColor)
{
$coords = imagettftext($this->im, $fontsize, $degree,
$x + $this->conf['scale'], $y + $this->conf['scale'],
$this->GdShadowColor, $fontfile, $letter);
}
$coords = imagettftext($this->im, $fontsize, $degree,
$x, $y,
$this->GdFgColor, $fontfile, $letter);
$x += ($coords[2] - $x) + ($fontcfg['spacing'] * $this->conf['scale']);
}
}

/**
* Wave filter
*/
protected function WaveImage()
{
// X-axis wave generation
$xp = $this->conf['scale'] * $this->Xperiod * rand(1, 3);
$k = rand(0, 100);
for ($i = 0; $i < ($this->conf['width'] * $this->conf['scale']); $i++)
{
imagecopy($this->im, $this->im,
$i-1, sin($k + $i / $xp) * ($this->conf['scale'] * $this->Xamplitude),
$i, 0, 1, $this->conf['height'] * $this->conf['scale']);
}
// Y-axis wave generation
$k = rand(0, 100);
$yp = $this->conf['scale'] * $this->Yperiod * rand(1, 2);
for ($i = 0; $i < ($this->conf['height'] * $this->conf['scale']); $i++)
{
imagecopy($this->im, $this->im,
sin($k + $i / $yp) * ($this->conf['scale'] * $this->Yamplitude), $i-1,
0, $i, $this->conf['width'] * $this->conf['scale'], 1);
}
}

/**
* Reduce the image to the final size
*/
protected function ReduceImage()
{
$imResampled = imagecreatetruecolor($this->conf['width'], $this->conf['height']);
imagecopyresampled($imResampled, $this->im,
0, 0, 0, 0,
$this->conf['width'], $this->conf['height'],
$this->conf['width'] * $this->conf['scale'], $this->conf['height'] * $this->conf['scale']
);
imagedestroy($this->im);
$this->im = $imResampled;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,84 @@
<?php
class LtConfig
{
public $storeHandle;
protected $conf;

public function __construct()
{
if (!is_object($this->storeHandle))
{
$this->storeHandle = new LtStoreMemory;
}
}

public function init()
{
//don't removeme, I am the placeholder
}

public function get($key)
{
$storedConfig = $this->storeHandle->get($key);
if ($storedConfig instanceof LtConfigExpression)
{
$str = $storedConfig->__toString();
if ($storedConfig->autoRetrived)
{
eval("\$value=$str;");
return $value;
}
else
{
return $str;
}
}
else
{
return $storedConfig;
}
}

/**
* 警告
* 这里会包含两个用户定义的配置文件,为了不和配置文件里的变量名发生重名
* 本方法不定义和使用变量名
*/
public function loadConfigFile($configFile)
{
if (0 == $this->storeHandle->get(".config_total"))
{
if (null === $configFile || !is_file($configFile))
{
trigger_error("no config file specified or invalid config file");
}
$this->conf = include($configFile);
if (!is_array($this->conf))
{
trigger_error("config file do NOT return array: $configFile");
}
elseif (!empty($this->conf))
{
if (0 == $this->storeHandle->get(".config_total"))
{
$this->storeHandle->add(".config_total", 0);
}
$this->addConfig($this->conf);
}
}
}

public function addConfig($configArray)
{
foreach($configArray as $key => $value)
{
if (!$this->storeHandle->update($key, $value))
{
if ($this->storeHandle->add($key, $value))
{
$this->storeHandle->update(".config_total", $this->storeHandle->get(".config_total") + 1, 0);
}
}
}
}
}

View file

@ -0,0 +1,17 @@
<?php
class LtConfigExpression
{
private $_expression;
public $autoRetrived;
public function __construct($string, $autoRetrived = true)
{
$this->_expression = (string) $string;
$this->autoRetrived = $autoRetrived;
}
public function __toString()
{
return $this->_expression;
}
}

View file

@ -0,0 +1,145 @@
<?php
class LtCookie
{
public $configHandle;
private $secretKey;

public function __construct()
{
if (! $this->configHandle instanceof LtConfig)
{
if (class_exists("LtObjectUtil", false))
{
$this->configHandle = LtObjectUtil::singleton("LtConfig");
}
else
{
$this->configHandle = new LtConfig;
}
}
}

public function init()
{
$this->secretKey = $this->configHandle->get("cookie.secret_key");
if(empty($this->secretKey))
{
trigger_error("cookie.secret_key empty");
}
}

/**
* Decrypt the encrypted cookie
*
* @param string $encryptedText
* @return string
*/
protected function decrypt($encryptedText)
{
error_log(__METHOD__ . ' mcrypt is deprecated - return parameter as is. Implement OpenSSL if cookie encryption is necessary.');

return $encryptedText;

// $key = $this->secretKey;
// $cryptText = base64_decode($encryptedText);
// $ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
// $iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
// $decryptText = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $cryptText, MCRYPT_MODE_ECB, $iv);
// return trim($decryptText);
}

/**
* Encrypt the cookie
*
* @param string $plainText
* @return string
*/
protected function encrypt($plainText)
{
error_log(__METHOD__ . ' mcrypt is deprecated - return parameter as is. Implement OpenSSL if cookie encryption is necessary.');

return $plainText;

// $key = $this->secretKey;
// $ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
// $iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
// $encryptText = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plainText, MCRYPT_MODE_ECB, $iv);
// return trim(base64_encode($encryptText));
//
}

/**
* Set cookie value to deleted with $name
*
* @param array $args
* @return boolean
*/
public function delCookie($name, $path = '/', $domain = null)
{
if (isset($_COOKIE[$name]))
{
if (is_array($_COOKIE[$name]))
{
foreach($_COOKIE[$name] as $k => $v)
{
setcookie($name . '[' . $k . ']', '', time() - 86400, $path, $domain);
}
}
else
{
setcookie($name, '', time() - 86400, $path, $domain);
}
}
}

/**
* Get cookie value with $name
*
* @param string $name
* @return mixed
*/
public function getCookie($name)
{
$ret = null;
if (isset($_COOKIE[$name]))
{
if (is_array($_COOKIE[$name]))
{
$ret = array();
foreach($_COOKIE[$name] as $k => $v)
{
$v = $this->decrypt($v);
$ret[$k] = $v;
}
}
else
{
$ret = $this->decrypt($_COOKIE[$name]);
}
}
return $ret;
}

/**
* Set cookie
*
* @param array $args
* @return boolean
*/
public function setCookie($name, $value = '', $expire = null, $path = '/', $domain = null, $secure = 0)
{
if (is_array($value))
{
foreach($value as $k => $v)
{
$v = $this->encrypt($v);
setcookie($name . '[' . $k . ']', $v, $expire, $path, $domain, $secure);
}
}
else
{
$value = $this->encrypt($value);
setcookie($name, $value, $expire, $path, $domain, $secure);
}
}
}

View file

@ -0,0 +1,12 @@
<?php
interface LtDbConnectionAdapter
{
/**
* @todo 兼容使用Unix Domain Socket方式连接数据库可以不指定port
*/
public function connect($connConf);
public function exec($sql, $connResource);
public function query($sql, $connResource);
public function lastInsertId($connResource);
public function escape($sql, $connResource);
}

View file

@ -0,0 +1,34 @@
<?php
class LtDbConnectionAdapterMysql implements LtDbConnectionAdapter
{
public function connect($connConf)
{
return mysql_connect($connConf["host"] . ":" . $connConf["port"], $connConf["username"], $connConf["password"]);
}

public function exec($sql, $connResource)
{
return mysql_query($sql, $connResource) ? mysql_affected_rows($connResource) : false;
}

public function query($sql, $connResource)
{
$result = mysql_query($sql, $connResource);
$rows = array();
while($row = mysql_fetch_assoc($result))
{
$rows[] = $row;
}
return $rows;
}

public function lastInsertId($connResource)
{
return mysql_insert_id($connResource);
}

public function escape($sql, $connResource)
{
return mysql_real_escape_string($sql, $connResource);
}
}

View file

@ -0,0 +1,35 @@
<?php
class LtDbConnectionAdapterMysqli implements LtDbConnectionAdapter
{
public function connect($connConf)
{
return new mysqli($connConf["host"], $connConf["username"], $connConf["password"], $connConf["dbname"], $connConf["port"]);
}

public function exec($sql, $connResource)
{
$connResource->query($sql);
return $connResource->affected_rows;
}

public function query($sql, $connResource)
{
$rows = array();
$result = $connResource->query($sql);
while($row = $result->fetch_assoc())
{
$rows[] = $row;
}
return $rows;
}

public function lastInsertId($connResource)
{
return $connResource->insert_id;
}

public function escape($sql, $connResource)
{
return mysqli_real_escape_string($connResource, $sql);
}
}

View file

@ -0,0 +1,65 @@
<?php
class LtDbConnectionAdapterPdo implements LtDbConnectionAdapter
{
public function connect($connConf)
{
// $option = array(PDO::ATTR_PERSISTENT => true);
if (isset($connConf['pconnect']) && true == $connConf['pconnect'])
{
$option[PDO::ATTR_PERSISTENT] = true;
}
else
{
$option[PDO::ATTR_PERSISTENT] = false;
}
switch ($connConf['adapter'])
{
case "pdo_mysql":
$dsn = "mysql:host={$connConf['host']};dbname={$connConf['dbname']}";
break;
case "pdo_sqlite":
$connConf["host"] = rtrim($connConf["host"], '\\/') . DIRECTORY_SEPARATOR;
if (!is_dir($connConf["host"]))
{
if (!@mkdir($connConf["host"], 0777, true))
{
trigger_error("Can not create {$connConf['host']}");
}
}
$dsn = "{$connConf['sqlite_version']}:{$connConf['host']}{$connConf['dbname']}";
break;
case "pdo_pgsql":
$dsn = "pgsql:host={$connConf['host']} port={$connConf['port']} dbname={$connConf['dbname']} user={$connConf['username']} password={$connConf['password']}";
break;
case "odbc":
$dsn = "odbc:" . $connConf["host"];
break;
}
return new PDO($dsn, $connConf['username'], $connConf['password'], $option);
}

public function exec($sql, $connResource)
{
return $connResource->exec($sql);
}

public function query($sql, $connResource)
{
return $connResource->query($sql)->fetchAll(PDO::FETCH_ASSOC);
}

/**
*
* @todo pgsql support
*/
public function lastInsertId($connResource)
{
return $connResource->lastInsertId();
}

public function escape($sql, $connResource)
{
// quote返回值带最前面和最后面的单引号, 这里去掉, DbHandler中加
return trim($connResource->quote($sql), "'");
}
}

View file

@ -0,0 +1,46 @@
<?php
class LtDbConnectionAdapterPgsql implements LtDbConnectionAdapter
{
public function connect($connConf)
{
if (isset($connConf['pconnect']) && true == $connConf['pconnect'])
{
$func = 'pg_pconnect';
}
else
{
$func = 'pg_connect';
}
return $func("host={$connConf['host']} port={$connConf['port']} user={$connConf['username']} password={$connConf['password']}");
}

public function exec($sql, $connResource)
{
$result = pg_query($connResource, $sql);
return pg_affected_rows($result);
}

public function query($sql, $connResource)
{
$result = pg_query($connResource, $sql);
return pg_fetch_all($result);
}

// SELECT CURRVAL(
// pg_get_serial_sequence('my_tbl_name','id_col_name'));"
// ------------------------------------------------------
// CREATE FUNCTION last_insert_id() RETURNS bigint AS $$
// SELECT lastval();
// $$ LANGUAGE SQL VOLATILE;
public function lastInsertId($connResource)
{
$result = pg_query($connResource, "SELECT lastval()");
$row = pg_fetch_array($result, 0, PGSQL_NUM);
return $row[0];
}

public function escape($sql, $connResource)
{
return pg_escape_string($sql);
}
}

View file

@ -0,0 +1,72 @@
<?php
/**
* Sqlite 预定义了类 SQLiteDatabase 本实现没有使用。
* 这里使用的全部是过程函数。
* 无论是函数还是类本实现只支持sqlite的2.x系列版本。
* php5.3新增扩展sqlite3用来支持3.x版本。
* PDO则同时支持2.x和3.x版本。
*/
class LtDbConnectionAdapterSqlite implements LtDbConnectionAdapter
{
public function connect($connConf)
{
if (isset($connConf['pconnect']) && true == $connConf['pconnect'])
{
$func = 'sqlite_popen';
}
else
{
$func = 'sqlite_open';
}
$connConf["host"] = rtrim($connConf["host"], '\\/') . DIRECTORY_SEPARATOR;
if(!is_dir($connConf["host"]))
{
if(!@mkdir($connConf["host"], 0777, true))
{
trigger_error("Can not create {$connConf['host']}");
}
}
$error = '';
$connResource = $func($connConf["host"] . $connConf["dbname"], 0666, $error);
if (!$connResource)
{
trigger_error($error, E_USER_ERROR);
}
else
{
return $connResource;
}
}

public function exec($sql, $connResource)
{
if(empty($sql))
{
return 0;
}
sqlite_exec($connResource, $sql);
// echo '<pre>';
// print_r(debug_backtrace());
// debug_print_backtrace();
// echo '</pre>';
// delete from table 结果为0原因未知。
// 使用 delete from table where 1 能返回正确结果
return sqlite_changes($connResource);
}

public function query($sql, $connResource)
{
$result = sqlite_query($connResource, $sql, SQLITE_ASSOC);
return sqlite_fetch_all($result, SQLITE_ASSOC);
}

public function lastInsertId($connResource)
{
return sqlite_last_insert_rowid($connResource);
}

public function escape($sql, $connResource)
{
return sqlite_escape_string($sql);
}
}

View file

@ -0,0 +1,31 @@
<?php
interface LtDbSqlAdapter
{
/**
* Return SQL statements
*/
public function setCharset($charset);
public function setSchema($schema);

public function showSchemas($database);
public function showTables($schema);
public function showFields($table);

public function beginTransaction();
public function commit();
public function rollBack();

public function limit($limit, $offset);

/**
* Retrive recordset
*/
public function getSchemas($queryResult);
public function getTables($queryResult);
public function getFields($queryResult);

/**
* Parse SQL
*/
public function detectQueryType($sql);
}

View file

@ -0,0 +1,90 @@
<?php
class LtDbSqlAdapterMysql implements LtDbSqlAdapter
{
public function setCharset($charset)
{
return "SET NAMES " . str_replace('-', '', $charset);
}
public function setSchema($schema)
{
return "USE $schema";
}

public function showSchemas($database)
{
return "SHOW DATABASES";
}
public function showTables($schema)
{
return "SHOW TABLES";
}
public function showFields($table)
{
return "DESCRIBE $table";
}

public function beginTransaction()
{
return "START TRANSACTION";
}
public function commit()
{
return "COMMIT";
}
public function rollBack()
{
return "ROLLBACK";
}

public function limit($limit, $offset)
{
return " LIMIT $limit OFFSET $offset";
}
public function getSchemas($queryResult)
{

}
public function getTables($queryResult)
{

}
public function getFields($queryResult)
{
foreach ($queryResult as $key => $value)
{
$fields[$value['Field']]['name'] = $value['Field'];
$fields[$value['Field']]['type'] = $value['Type'];
/*
* not null is NO or empty, null is YES
*/
$fields[$value['Field']]['notnull'] = (bool) ($value['Null'] != 'YES');
$fields[$value['Field']]['default'] = $value['Default'];
$fields[$value['Field']]['primary'] = (strtolower($value['Key']) == 'pri');
}
return $fields;
}
public function detectQueryType($sql)
{
if (preg_match("/^\s*SELECT|^\s*EXPLAIN|^\s*SHOW|^\s*DESCRIBE/i", $sql))
{
$ret = 'SELECT';
}
else if (preg_match("/^\s*INSERT/i", $sql))
{
$ret = 'INSERT';
}
else if (preg_match("/^\s*UPDATE|^\s*DELETE|^\s*REPLACE/i", $sql))
{
$ret = 'CHANGE_ROWS';
}
else if (preg_match("/^\s*USE|^\s*SET/i", $sql))
{
$ret = 'SET_SESSION_VAR';
}
else
{
$ret = 'OTHER';
}
return $ret;
}
}

View file

@ -0,0 +1,81 @@
<?php
class LtDbSqlAdapterPgsql implements LtDbSqlAdapter
{
public function setCharset($charset)
{
return "SET client_encoding TO '$charset'";
}
public function setSchema($schema)
{
return "SET search_path TO $schema";
}

public function beginTransaction()
{
return "";
}
public function commit()
{
return "";
}
public function rollBack()
{
return "";
}

public function showSchemas($database)
{

}
public function showTables($schema)
{
return "SELECT case when n.nspname='public' then c.relname else n.nspname||'.'||c.relname end as relname
FROM pg_class c join pg_namespace n on (c.relnamespace=n.oid)
WHERE c.relkind = 'r'
AND n.nspname NOT IN ('information_schema','pg_catalog')
AND n.nspname NOT LIKE 'pg_temp%'
AND n.nspname NOT LIKE 'pg_toast%'
ORDER BY relname";
}
public function showFields($table)
{
return "SELECT a.attnum, a.attname AS field, t.typname AS type,
format_type(a.atttypid, a.atttypmod) AS complete_type,
a.attnotnull AS isnotnull,
( SELECT 't' FROM pg_index
WHERE c.oid = pg_index.indrelid
AND pg_index.indkey[0] = a.attnum
AND pg_index.indisprimary = 't') AS pri,
(SELECT pg_attrdef.adsrc FROM pg_attrdef
WHERE c.oid = pg_attrdef.adrelid
AND pg_attrdef.adnum=a.attnum) AS default
FROM pg_attribute a, pg_class c, pg_type t
WHERE c.relname = '$table'
AND a.attnum > 0
AND a.attrelid = c.oid
AND a.atttypid = t.oid
ORDER BY a.attnum";
}

public function limit($limit, $offset)
{
return " LIMIT $limit OFFSET $offset";
}

public function getSchemas($queryResult)
{
}
public function getTables($queryResult)
{
}
public function getFields($queryResult)
{
}
public function detectQueryType($sql)
{
}
}

View file

@ -0,0 +1,120 @@
<?php
class LtDbSqlAdapterSqlite implements LtDbSqlAdapter
{
public function setCharset($charset)
{
// return 'PRAGMA encoding = "' . $charset . '"';
return '';
}
public function setSchema($schema)
{
return '';
}

public function beginTransaction()
{
return 'BEGIN TRANSACTION';
}

public function commit()
{
return 'COMMIT TRANSACTION';
}

public function rollBack()
{
return 'ROLLBACK TRANSACTION';
}

public function showSchemas($database)
{
//return "SHOW DATABASES";
return '';
}
public function showTables($schema)
{
// 临时表及其索引不在 SQLITE_MASTER 表中而在 SQLITE_TEMP_MASTER 中出现
return "SELECT name FROM sqlite_master WHERE type='table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='table' ORDER BY name";
}
public function showFields($table)
{
return "PRAGMA table_info('" . $table . "')";

}
public function limit($limit, $offset)
{
return " LIMIT $limit OFFSET $offset";
}

public function getSchemas($queryResult)
{
}
public function getTables($queryResult)
{
return $queryResult;
}
public function getFields($queryResult)
{
$fields = array();
foreach ($queryResult as $key => $value)
{
// 字段名
$fields[$value['name']]['name'] = $value['name'];
// 字段类型
$fulltype = $value['type'];
$size = null;
$precision = null;
$scale = null;

if (preg_match('/^([^\(]+)\(\s*(\d+)\s*,\s*(\d+)\s*\)$/',$fulltype, $matches))
{
$type = $matches[1];
$precision = $matches[2];
$scale = $matches[3]; // aka precision
}
elseif (preg_match('/^([^\(]+)\(\s*(\d+)\s*\)$/',$fulltype, $matches))
{
$type = $matches[1];
$size = $matches[2];
}
else
{
$type = $fulltype;
}

$fields[$value['name']]['type'] = $type;
/**
* not null is 99, null is 0
*/
$fields[$value['name']]['notnull'] = (bool) ($value['notnull'] != 0);
$fields[$value['name']]['default'] = $value['dflt_value'];
$fields[$value['name']]['primary'] = (bool) ($value['pk'] == 1 && strtoupper($fulltype) == 'INTEGER');
}
return $fields;
}
public function detectQueryType($sql)
{
if (preg_match("/^\s*SELECT|^\s*PRAGMA/i", $sql))
{
$ret = 'SELECT';
}
else if (preg_match("/^\s*INSERT/i", $sql))
{
$ret = 'INSERT';
}
else if (preg_match("/^\s*UPDATE|^\s*DELETE|^\s*REPLACE/i", $sql))
{
$ret = 'CHANGE_ROWS';
}
else if (preg_match("/^\s*USE|^\s*SET/i", $sql))
{
$ret = 'SET_SESSION_VAR';
}
else
{
$ret = 'OTHER';
}
return $ret;
}
}

View file

@ -0,0 +1,91 @@
<?php
class LtDb
{
public $configHandle;

public $group;
public $node;
protected $dbh;

public function __construct()
{
if (! $this->configHandle instanceof LtConfig)
{
if (class_exists("LtObjectUtil", false))
{
$this->configHandle = LtObjectUtil::singleton("LtConfig");
}
else
{
$this->configHandle = new LtConfig;
}
}
}

public function init()
{
$this->dbh = new LtDbHandle;
$this->dbh->configHandle = $this->configHandle;
$this->dbh->group = $this->getGroup();
$this->dbh->node = $this->getNode();
$this->dbh->init();
}

public function getDbHandle()
{
return $this->dbh;
}

public function getTDG($tableName)
{
$tg = new LtDbTableDataGateway;
$tg->configHandle = $this->configHandle;
$tg->tableName = $tableName;
$tg->createdColumn = 'created';
$tg->modifiedColumn = 'modified';
$tg->dbh = $this->dbh;
return $tg;
}

public function getSqlMapClient()
{
$smc = new LtDbSqlMapClient;
$smc->configHandle = $this->configHandle;
$smc->dbh = $this->dbh;
return $smc;
}

public function changeNode($node)
{
$this->node = $node;
$this->dbh->node = $node;
}

protected function getGroup()
{
if ($this->group)
{
return $this->group;
}
$servers = $this->configHandle->get("db.servers");
if (1 == count($servers))
{
return key($servers);
}
return false;
}

protected function getNode()
{
if ($this->node)
{
return $this->node;
}
$servers = $this->configHandle->get("db.servers");
if (1 == count($servers[$this->getGroup()]))
{
return key($servers[$this->getGroup()]);
}
return false;
}
}

View file

@ -0,0 +1,15 @@
<?php
class LtDbAdapterFactory
{
public function getConnectionAdapter($connectionAdapterType)
{
$LtDbConnectionAdapter = "LtDbConnectionAdapter" . ucfirst($connectionAdapterType);
return new $LtDbConnectionAdapter;
}

public function getSqlAdapter($sqlAdapterType)
{
$LtDbSqlAdapter = "LtDbSqlAdapter" . ucfirst($sqlAdapterType);
return new $LtDbSqlAdapter;
}
}

View file

@ -0,0 +1,122 @@
<?php
class LtDbConfigBuilder
{
protected $servers = array();

protected $tables = array();

protected $adapters = array(
//"php_ext" => array("connection_adapter" => "", "sql_adapter" => "")
"pgsql" => array("connection_adapter" => "pgsql", "sql_adapter" => "pgsql"),
"pdo_pgsql" => array("connection_adapter" => "pdo", "sql_adapter" => "pgsql"),
"oci" => array("connection_adapter" => "oci", "sql_adapter" => "oracle"),
"pdo_oci" => array("connection_adapter" => "pdo", "sql_adapter" => "oracle"),
"mssql" => array("connection_adapter" => "mssql", "sql_adapter" => "mssql"),
"pdo_dblib" => array("connection_adapter" => "pdo", "sql_adapter" => "mssql"),
"mysql" => array("connection_adapter" => "mysql", "sql_adapter" => "mysql"),
"mysqli" => array("connection_adapter" => "mysqli", "sql_adapter" => "mysql"),
"pdo_mysql" => array("connection_adapter" => "pdo", "sql_adapter" => "mysql"),
"sqlite" => array("connection_adapter" => "sqlite", "sql_adapter" => "sqlite"),
"sqlite3" => array("connection_adapter" => "sqlite3", "sql_adapter" => "sqlite"),
"pdo_sqlite" => array("connection_adapter" => "pdo", "sql_adapter" => "sqlite"),
);

protected $defaultConfig = array(
"host" => "localhost", //some ip, hostname
//"port" => 3306,
"username" => "root",
"password" => null,
//"adapter" => "mysql", //mysql,mysqli,pdo_mysql,sqlite,pdo_sqlite
"charset" => "UTF-8",
"pconnect" => true, //true,false
"connection_ttl" => 3600, //any seconds
"dbname" => null, //default dbname
"schema" => null, //default schema
"connection_adapter" => null,
"sql_adapter" => null,
);

protected $defaultAdapterConfigs = array(
"pgsql" => array(
"port" => 5432,
),
"oracle" => array(
"port" => 1521,
),
"mssql" => array(
"port" => 1433,
),
"mysql" => array(
"port" => 3306,
"pconnect" => false,
"connection_ttl" => 30,
),
);

public function addSingleHost($hostConfig)
{
$this->addHost("group_0", "node_0", "master", $hostConfig);
}

public function addHost($groupId, $nodeId = "node_0", $role = "master", $hostConfig)
{
if (isset($this->servers[$groupId][$nodeId][$role]))
{//以相同role的第一个host为默认配置
$ref = $this->servers[$groupId][$nodeId][$role][0];
}
else if ("slave" == $role && isset($this->servers[$groupId][$nodeId]["master"]))
{//slave host以master的第一个host为默认配置
$ref = $this->servers[$groupId][$nodeId]["master"][0];
}
else if (isset($this->servers[$groupId]) && count($this->servers[$groupId]))
{//以本group第一个node的master第一个host为默认配置
$refNode = key($this->servers[$groupId]);
$ref = $this->servers[$groupId][$refNode]["master"][0];
}
else
{
if (!isset($hostConfig["adapter"]))
{
trigger_error("No db adapter specified");
}
$ref = $this->defaultConfig;
if (isset($this->defaultAdapterConfigs[$this->adapters[$hostConfig["adapter"]]["sql_adapter"]]))
{
$ref = array_merge($ref, $this->defaultAdapterConfigs[$this->adapters[$hostConfig["adapter"]]["sql_adapter"]]);
}
}
$conf = array_merge($ref, $hostConfig);
$conf = array_merge($conf, $this->adapters[$conf["adapter"]]);
$conf = $this->convertDbnameToSchema($conf);
$this->servers[$groupId][$nodeId][$role][] = $conf;
}

public function getServers()
{
return $this->servers;
}

public function getTables()
{
return $this->tables;
}

public function buildTablesConfig()
{

}

/**
* Convert dbname to schema for: FrontBase, MySQL, mSQL, MS SQL Server, MaxDB, Sybase
* See: http://www.php.net/manual-lookup.php?pattern=_select_db
*/
protected function convertDbnameToSchema($conf)
{
if (preg_match("/fbsql|mysql|msql|mssql|maxdb|sybase/i", $conf["sql_adapter"]) && isset($conf["dbname"]))
{
$conf["schema"] = $conf["dbname"];
$conf["dbname"] = null;
}
return $conf;
}
}

View file

@ -0,0 +1,117 @@
<?php
class LtDbConnectionManager
{
/**
* Connection management
* array(
* "connection" => connection resource id,
* "expire_time" => expire time,
* "schema" => default schema name,
* "charset" => char set / encoding
* )
*/
static public $connectionPool;
public $configHandle;
protected $connectionAdapter;
protected $sqlAdapter;
private $servers;

public function getConnection($group, $node, $role = "master")
{
if(empty($this->servers))
{
$this->servers = $this->configHandle->get("db.servers");
}
if (($connection = $this->getNewConnection($group, $node, $role)) ||($connection = $this->getCachedConnection($group, $node, $role)))
{
return array(
"connectionAdapter" => $this->connectionAdapter,
"connectionResource" => $connection
);
}
else
{
trigger_error("db server can not be connected: group=$group, node=$node, role=$role", E_USER_ERROR);
return false;
}
}

protected function getConnectionKey($connConf)
{
return $connConf['adapter'] . $connConf['host'] . $connConf['port'] . $connConf['username'] . $connConf['dbname'];
}

protected function saveConnection($connConf, $connection, $ttl)
{
$connectionInfo = array(
"connection" => $connection,
"expire_time" => time() + $ttl,
"schema" => $connConf["schema"],
"charset" => $connConf["charset"],
);
self::$connectionPool[$this->getConnectionKey($connConf)] = $connectionInfo;
}

protected function getCachedConnection($group, $node, $role)
{
foreach($this->servers[$group][$node][$role] as $hostConfig)
{
$key = $this->getConnectionKey($hostConfig);
if(isset(self::$connectionPool[$key]) && time() < self::$connectionPool[$key]['expire_time'])
{//cached connection resource FOUND
$connectionInfo = self::$connectionPool[$key];
if ($connectionInfo["schema"] != $hostConfig["schema"] || $connectionInfo["charset"] != $hostConfig["charset"])
{//检查当前schema和charset与用户要操作的目标不一致
$hostConfig = $this->servers[$group][$node][$role][$hostIndexArray[$hashNumber]];
$dbFactory = new LtDbAdapterFactory;
$this->connectionAdapter = $dbFactory->getConnectionAdapter($hostConfig["connection_adapter"]);
$this->sqlAdapter = $dbFactory->getSqlAdapter($hostConfig["sql_adapter"]);
if ($connectionInfo["schema"] != $hostConfig["schema"])
{
$this->connectionAdapter->exec($this->sqlAdapter->setSchema($hostConfig["schema"]), $connectionInfo["connection"]);
}
if ($connectionInfo["charset"] != $hostConfig["charset"])
{
$this->connectionAdapter->exec($this->sqlAdapter->setCharset($hostConfig["charset"]), $connectionInfo["connection"]);
}
$this->saveConnection($hostConfig, $connectionInfo["connection"], $hostConfig["connection_ttl"]);
}
return $connectionInfo["connection"];
}
}
return false;
}

protected function getNewConnection($group, $node, $role)
{
$hostTotal = count($this->servers[$group][$node][$role]);
$hostIndexArray = array_keys($this->servers[$group][$node][$role]);
while ($hostTotal)
{
$hashNumber = substr(microtime(),7,1) % $hostTotal;
$hostConfig = $this->servers[$group][$node][$role][$hostIndexArray[$hashNumber]];
$dbFactory = new LtDbAdapterFactory;
$this->connectionAdapter = $dbFactory->getConnectionAdapter($hostConfig["connection_adapter"]);
$this->sqlAdapter = $dbFactory->getSqlAdapter($hostConfig["sql_adapter"]);
if ($connection = $this->connectionAdapter->connect($hostConfig))
{
$this->connectionAdapter->exec($this->sqlAdapter->setSchema($hostConfig["schema"]), $connection);
$this->connectionAdapter->exec($this->sqlAdapter->setCharset($hostConfig["charset"]), $connection);
$this->saveConnection($hostConfig, $connection, $hostConfig["connection_ttl"]);
return $connection;
}
else
{
//trigger_error('connection fail', E_USER_WARNING);
//delete the unavailable server
for ($i = $hashNumber; $i < $hostTotal - 1; $i ++)
{
$hostIndexArray[$i] = $hostIndexArray[$i+1];
}
unset($hostIndexArray[$hostTotal-1]);
$hostTotal --;
}//end else
}//end while
return false;
}
}

View file

@ -0,0 +1,200 @@
<?php
class LtDbHandle
{
public $configHandle;
public $group;
public $node;
public $role = "master";
public $connectionAdapter;
public $connectionResource;
public $sqlAdapter;
protected $connectionManager;
private $servers;

public function __construct()
{
}

public function init()
{
if(empty($this->servers))
{
$this->servers = $this->configHandle->get("db.servers");
}
$this->connectionManager = new LtDbConnectionManager;
$this->connectionManager->configHandle = $this->configHandle;
$this->sqlAdapter = $this->getCurrentSqlAdapter();
$connectionInfo = $this->connectionManager->getConnection($this->group, $this->node, $this->role);
$this->connectionAdapter = $connectionInfo["connectionAdapter"];
$this->connectionResource = $connectionInfo["connectionResource"];
}

/**
* Trancaction methods
*/
public function beginTransaction()
{
return $this->connectionAdapter->exec($this->sqlAdapter->beginTransaction(), $this->connectionResource);
}

public function commit()
{
return $this->connectionAdapter->exec($this->sqlAdapter->commit(), $this->connectionResource);
}

public function rollBack()
{
return $this->connectionAdapter->exec($this->sqlAdapter->rollBack(), $this->connectionResource);
}

/**
* Execute an sql query
*
* @param $sql
* @param $bind
* @param $forceUseMaster
* @return false on query failed
* --sql type-- --return value--
* SELECT, SHOW, DESECRIBE, EXPLAIN rowset or NULL when no record found
* INSERT the ID generated for an AUTO_INCREMENT column
* UPDATE, DELETE, REPLACE affected count
* USE, DROP, ALTER, CREATE, SET etc true
* @notice 每次只能执行一条SQL
* 不要通过此接口执行USE DATABASE, SET NAMES这样的语句
*/
public function query($sql, $bind = null, $forceUseMaster = false)
{
$sql = trim($sql);
if (empty($sql))
{
trigger_error('Empty the SQL statement');
}
$queryType = $this->sqlAdapter->detectQueryType($sql);
switch ($queryType)
{
case "SELECT":
if (!$forceUseMaster && isset($this->servers[$this->group][$this->node]["slave"]))
{
$this->role = "slave";
}
$queryMethod = "select";
break;
case "INSERT":
$this->role = "master";
$queryMethod = "insert";
break;
case "CHANGE_ROWS":
$this->role = "master";
$queryMethod = "changeRows";
break;
case "SET_SESSION_VAR":
$queryMethod = "setSessionVar";
break;
case "OTHER":
default:
$this->role = "master";
$queryMethod = "other";
break;
}
$connectionInfo = $this->connectionManager->getConnection($this->group, $this->node, $this->role);
$this->connectionAdapter = $connectionInfo["connectionAdapter"];
$this->connectionResource = $connectionInfo["connectionResource"];
if (is_array($bind) && 0 < count($bind))
{
$sql = $this->bindParameter($sql, $bind);
}
return $this->$queryMethod($sql, $this->connectionResource);
}
/**
* function posted by renlu
*/
public function escape($str)
{
return $this->connectionAdapter->escape($str, $this->connectionResource);
}
/**
* function posted by renlu
*/
public function insertid()
{
return $this->connectionAdapter->lastInsertId($this->connectionResource);
}
/**
* Generate complete sql from sql template (with placeholder) and parameter
*
* @param $sql
* @param $parameter
* @return string
* @todo 兼容pgsql等其它数据库pgsql的某些数据类型不接受单引号引起来的值
*/
public function bindParameter($sql, $parameter)
{
// 注意替换结果尾部加一个空格
$sql = preg_replace("/:([a-zA-Z0-9_\-\x7f-\xff][a-zA-Z0-9_\-\x7f-\xff]*)\s*([,\)]?)/", "\x01\x02\x03\\1\x01\x02\x03\\2 ", $sql);
foreach($parameter as $key => $value)
{
$find[] = "\x01\x02\x03$key\x01\x02\x03";
if ($value instanceof LtDbSqlExpression)
{
$replacement[] = $value->__toString();
}
else
{
$replacement[] = "'" . $this->connectionAdapter->escape($value, $this->connectionResource) . "'";
}
}
$sql = str_replace($find, $replacement, $sql);
return $sql;
}

protected function getCurrentSqlAdapter()
{
$factory = new LtDbAdapterFactory;
$host = key($this->servers[$this->group][$this->node][$this->role]);
return $factory->getSqlAdapter($this->servers[$this->group][$this->node][$this->role][$host]["sql_adapter"]);
}

protected function select($sql, $connResource)
{
$result = $this->connectionAdapter->query($sql, $connResource);
if (empty($result))
{
return null;
}
else
{
return $result;
}
}

protected function insert($sql, $connResource)
{
if ($result = $this->connectionAdapter->exec($sql, $connResource))
{
return $this->connectionAdapter->lastInsertId($connResource);
}
else
{
return $result;
}
}

protected function changeRows($sql, $connResource)
{
return $this->connectionAdapter->exec($sql, $connResource);
}

/**
*
* @todo 更新连接缓存
*/
protected function setSessionVar($sql, $connResource)
{
return false === $this->connectionAdapter->exec($sql, $connResource) ? false : true;
}

protected function other($sql, $connResource)
{
return false === $this->connectionAdapter->exec($sql, $connResource) ? false : true;
}
}

View file

@ -0,0 +1,15 @@
<?php
class LtDbSqlExpression
{
private $_expression;
public function __construct($string)
{
$this->_expression = (string) $string;
}
public function __toString()
{
return (string) $this->_expression;
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* 加工工厂类由开发者自行开发,继承自这个类
*/
abstract class LtAbstractDbSqlMapFilterObject {

// query()方法返回的结果集,用于加工的原料
public $result;

/**
* 需要被继承实现逻辑的操作类输入query()方法返回的结果集
* 经过处理后返回开发者定义的对象或结构
*/
abstract protected function process();
}

View file

@ -0,0 +1,14 @@
<?php
class LtDbSqlMapClient
{
public $configHandle;
public $dbh;

public function execute($mapId, $bind = null)
{
$sqlMap = $this->configHandle->get($this->dbh->group . "." . $mapId);
$forceUseMaster = isset($sqlMap["force_use_master"]) ? $sqlMap["force_use_master"] : false;
return $this->dbh->query($sqlMap["sql"], $bind, $forceUseMaster);
}
}

View file

@ -0,0 +1,24 @@
<?php
/**
* 用于加工DB句柄query方法返回的数组
* 开发者在一次会话中可配置多个Filter
*/
class LtDbSqlMapResultFactory {
// Filter列表
public $filters;
public $configHandle;

public function init() {
}

/**
* 工厂入口sql map client调用的方法
* 在这个方法中调用开发者自定义的
* LtAbstractSqlMapFilterObject.process()方法
* 可配置多个process方法
*/
public function run() {
}
}

View file

@ -0,0 +1,300 @@
<?php
/**
* Database Table abstract
*
* @todo pretty join support
*/
class LtDbTableDataGateway
{
public $configHandle;
public $dbh;
/**
* The created field name
*
* @var string
*/
public $createdColumn;

/**
* The modified field name
*
* @var string
*/
public $modifiedColumn;

/**
* The table name
*
* @var string
*/
public $tableName;

/**
* The fields array
*
* @var array
*/
protected $fields;

/**
* The primary key
*
* @var string
*/
protected $primaryKey;
protected $servers;

/**
* Build table's field list
*
* @return array
*/
protected function buildFieldList()
{
if (!empty($this->fields))
{
return true;
}
$servers = $this->configHandle->get('db.servers');
$group = $this->dbh->group;
$node = $this->dbh->node;
$role = $this->dbh->role;
$table = $this->tableName;
$host = key($servers[$group][$node][$role]);
$key = md5($group . $node . $role . $table . $host . $table);
if (!$value = $this->configHandle->get($key))
{
$sql = $this->dbh->sqlAdapter->showFields($this->tableName);
$rs = $this->dbh->query($sql);
$this->fields = $this->dbh->sqlAdapter->getFields($rs);
foreach($this->fields as $field)
{
if ($field['primary'] == 1)
{
$this->primaryKey = $field['name'];
break;
}
}

$value['fields'] = $this->fields;
$value['primaryKey'] = $this->primaryKey;
$this->configHandle->addConfig(array($key => $value));
}
else
{
$this->fields = $value['fields'];
$this->primaryKey = $value['primaryKey'];
}
}

/**
* A shortcut to SELECT COUNT(*) FROM table WHERE condition
*
* @param array $args
* @return integer
* @example count(array('expression' => 'id < :id', 'value' => array('id' => 10)));
*/
public function count($args = null)
{
$selectTemplate = 'SELECT COUNT(*) AS total FROM %s%s';
$where = isset($args['where']['expression']) ? ' WHERE ' . $args['where']['expression'] : '';
$bind = isset($args['where']['value']) ? $args['where']['value'] : array();
$join = isset($args['join']) ? ' ' . $args['join'] : '';
$sql = sprintf($selectTemplate, $this->tableName, $join . $where);
$queryResult = $this->dbh->query($sql, $bind);
return $queryResult[0]['total'];
}

/**
* Delete a row by primary key
*
* @param string $primaryKeyId
* @return string
* @example delete(10);
*/
public function delete($primaryKeyId)
{
$this->buildFieldList();
$where['expression'] = $this->primaryKey . '=:' . $this->primaryKey;
$where['value'][$this->primaryKey] = $primaryKeyId;
return $this->deleteRows($where);
}

/**
* Delete many rows from table
* Please use this method carefully!
*
* @param array $args
* @return integer
* @example deleteRows(array('expression' => "id > :id", 'value' => array('id' => 2)));
*/
public function deleteRows($args = null)
{
$deleteTemplate = 'DELETE FROM %s%s';
$where = isset($args['expression']) ? ' WHERE ' . $args['expression'] : '';
$bind = isset($args['value']) ? $args['value'] : array();
$sql = sprintf($deleteTemplate, $this->tableName, $where);
return $this->dbh->query($sql, $bind);
}

/**
* Fetch one row from table by primary key
*
* @param string $primaryKeyId
* @param array $args
* @param boolean $useSlave
* @return array
* @example fetch(10)
*/
public function fetch($primaryKeyId, $args = null, $useSlave = true)
{
$this->buildFieldList();
$fetchRowsArgs['where']['expression'] = $this->tableName . '.' . $this->primaryKey . '=:' . $this->primaryKey;
$fetchRowsArgs['where']['value'][$this->primaryKey] = $primaryKeyId;
$fetchRowsArgs['fields'] = isset($args['fields']) ? $args['fields'] : null;
$fetchRowsArgs['join'] = isset($args['join']) ? $args['join'] : null;
$fetchResult = $this->fetchRows($fetchRowsArgs, $useSlave);
return $fetchResult ? $fetchResult[0] : $fetchResult;
}

/**
* Fetch many rows from table
*
* @param array $args
* @param boolean $useSlave
* @return array
* @example fetchRows(array('where' => array('expression' => "id > :id", 'value' => array('id' => 2))));
*/
public function fetchRows($args = null, $useSlave = true)
{
$this->buildFieldList();
$selectTemplate = 'SELECT %s FROM %s%s';
$fields = isset($args['fields']) ? $args['fields'] : '*';
$where = isset($args['where']['expression']) ? ' WHERE ' . $args['where']['expression'] : '';
$bind = isset($args['where']['value']) ? $args['where']['value'] : array();
$join = isset($args['join']) ? ' ' . $args['join'] : '';
$orderby = isset($args['orderby']) ? ' ORDER BY ' . $args['orderby'] : '';
$groupby = isset($args['groupby']) ? ' GROUP BY ' . $args['groupby'] : '';
$sql = sprintf($selectTemplate, $fields, $this->tableName, $join . $where . $groupby . $orderby);
if (isset($args['limit']))
{
$offset = isset($args['offset']) ? $args['offset'] : 0;
$sql = $sql . ' ' . $this->dbh->sqlAdapter->limit($args['limit'], $offset);
}
return $this->dbh->query($sql, $bind);
}

/**
* Insert one row into table, then return the inserted row's pk
*
* @param array $args
* @return string
* @example insert(array('name' => 'lily', 'age' => '12'));
*/
public function insert($args = null)
{
$this->buildFieldList();
$insertTemplate = 'INSERT INTO %s (%s) VALUES (%s)';
$fields = array();
$placeHolders = array();
foreach($args as $field => $value)
{
if (isset($this->fields[$field]))
{
$fields[] = $field;
$placeholders[] = ":$field";
$values[$field] = $value;
}
}
if (isset($this->fields[$this->createdColumn]) && !isset($args[$this->createdColumn]))
{
$fields[] = $this->createdColumn;
$placeholders[] = ':' . $this->createdColumn;
$values[$this->createdColumn] = time();
}
if (isset($this->fields[$this->modifiedColumn]) && !isset($args[$this->modifiedColumn]))
{
$fields[] = $this->modifiedColumn;
$placeholders[] = ':' . $this->modifiedColumn;
$values[$this->modifiedColumn] = time();
}
$sql = sprintf($insertTemplate, $this->tableName, implode(",", $fields), implode(",", $placeholders));
$bind = $values;
$queryResult = $this->dbh->query($sql, $bind);
return isset($args[$this->primaryKey]) ? $args[$this->primaryKey] : $queryResult;
}

/**
* Update one row by primary key
*
* @param string $primaryKeyId
* @param array $args
* @return integer
* @example update(1, array('name' => 'lily', 'age' => '18'));
*/
public function update($primaryKeyId, $args = null)
{
$this->buildFieldList();
$where['expression'] = $this->primaryKey . '=:' . $this->primaryKey;
$where['value'][$this->primaryKey] = $primaryKeyId;
return $this->updateRows($where, $args);
}

/**
* Update manay rows
* Please use this method carefully!
*
* @param array $where
* @param array $args
* @return integer
* @example updateRows(array('expression' => "id > :id", 'value' => array('id' => 2)), array('name' => 'kiwi', 'age' => '1'));
*/
public function updateRows($where, $args = null)
{
$this->buildFieldList();
$updateTemplate = 'UPDATE %s SET %s%s';
$fields = array();
$bindParameters = array();
$placeholderStyle = isset($where['value']) && array_key_exists(0, $where['value']) ? 'questionMark' : 'named';
foreach($args as $field => $value)
{
if (isset($this->fields[$field]))
{
if ($args[$field] instanceof DbExpression)
{
$fields[] = "$field=" . $args[$field]->__toString();
}
else
{
if ('named' == $placeholderStyle)
{
$fields[] = "$field=:$field";
$bindParameters[$field] = $args[$field];
}
else
{
$fields[] = "$field=?";
$bindParameters[] = $args[$field];
}
}
}
}
if (isset($this->fields[$this->modifiedColumn]) && !isset($args[$this->modifiedColumn]))
{
if ('named' == $placeholderStyle)
{
$fields[] = $this->modifiedColumn . '=:' . $this->modifiedColumn;
$bindParameters[$this->modifiedColumn] = time();
}
else
{
$fields[] = $this->modifiedColumn . '=?';
$bindParameters[] = time();
}
}
$whereCause = isset($where['expression']) ? ' WHERE ' . $where['expression'] : '';
$bind = isset($where['value']) ? array_merge($bindParameters, $where['value']) : $bindParameters;
$sql = sprintf($updateTemplate, $this->tableName, implode(",", $fields), $whereCause);
return $this->dbh->query($sql, $bind);
}
}

View file

@ -0,0 +1,16 @@
<?php
class LtInflector
{
public $conf = array("separator" => "_");

public function camelize($uncamelized_words)
{
$uncamelized_words = $this->conf["separator"] . str_replace($this->conf["separator"] , " ", strtolower($uncamelized_words));
return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $this->conf["separator"] );
}

public function uncamelize($camelCaps)
{
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $this->conf["separator"] . "$2", $camelCaps));
}
}

View file

@ -0,0 +1,42 @@
<?php
class LtLogger
{
public $conf = array(
"separator" => "\t",
"log_file" => ""
);

private $fileHandle;

protected function getFileHandle()
{
if (null === $this->fileHandle)
{
if (empty($this->conf["log_file"]))
{
trigger_error("no log file spcified.");
}
$logDir = dirname($this->conf["log_file"]);
if (!is_dir($logDir))
{
mkdir($logDir, 0777, true);
}
$this->fileHandle = fopen($this->conf["log_file"], "a");
}
return $this->fileHandle;
}

public function log($logData)
{
if ("" == $logData || array() == $logData)
{
return false;
}
if (is_array($logData))
{
$logData = implode($this->conf["separator"], $logData);
}
$logData = $logData. "\n";
fwrite($this->getFileHandle(), $logData);
}
}

View file

@ -0,0 +1,157 @@
<?php
class Lotus
{
/**
* Lotus Option array
*
* @var array array(
* "proj_dir" =>
* "app_name" =>
* "autoload_dir" =>
* );
*/
public $option;
public $devMode = true;
public $defaultStoreDir;

protected $proj_dir;
protected $app_dir;
protected $data_dir;
protected $lotusRuntimeDir;
protected $coreCacheHandle;

public function __construct()
{
$this->lotusRuntimeDir = dirname(__FILE__) . DIRECTORY_SEPARATOR;
}

public function init()
{
$underMVC = false;
if (isset($this->option["proj_dir"]) && !empty($this->option["proj_dir"]))
{
$this->proj_dir = rtrim($this->option["proj_dir"], '\\/') . '/';
if (isset($this->option["app_name"]) && !empty($this->option["app_name"]))
{
$this->app_dir = $this->proj_dir . "app/" . $this->option["app_name"] . "/";
$this->data_dir = $this->proj_dir . "data/" . $this->option["app_name"] . "/";
$underMVC = true;
}
else
{
trigger_error("Lotus option [app_name] is missing.");
}
}

/**
* Load core component
*/
require_once $this->lotusRuntimeDir . "Store.php";
require_once $this->lotusRuntimeDir . "StoreMemory.php";
require_once $this->lotusRuntimeDir . "StoreFile.php";

if ($this->defaultStoreDir)
{
if ($defaultStoreDir = realpath($this->defaultStoreDir))
{
LtStoreFile::$defaultStoreDir = $defaultStoreDir;
}
else
{
trigger_error("invalid [default store dir]: " . $this->defaultStoreDir);
}
}
if (!$this->devMode)
{
/**
* accelerate LtAutoloader, LtConfig
*/
$this->coreCacheHandle = new LtStoreFile;
$prefix = sprintf("%u", crc32(serialize($this->app_dir)));
$this->coreCacheHandle->prefix = 'Lotus-' . $prefix;
$this->coreCacheHandle->useSerialize = true;
$this->coreCacheHandle->init();
}

/**
* Init Autoloader, do this before init all other lotusphp component.
*/
$this->prepareAutoloader();

/**
* init Config
*/
$this->prepareConfig();
/**
* Run dispatcher when under MVC mode
*/
if ($underMVC)
{
$this->runMVC();
}
}

/**
* Autoload all lotus components and user-defined libraries;
*/
protected function prepareAutoloader()
{
require_once $this->lotusRuntimeDir . "Autoloader/Autoloader.php";
$autoloader = new LtAutoloader;
$autoloader->autoloadPath[] = $this->lotusRuntimeDir;
if (isset($this->option["autoload_dir"]))
{
$autoloader->autoloadPath[] = $this->option["autoload_dir"];
}
if ($this->proj_dir)
{
is_dir($this->proj_dir . 'lib') && $autoloader->autoloadPath[] = $this->proj_dir . 'lib';
is_dir($this->app_dir . 'action') && $autoloader->autoloadPath[] = $this->app_dir . 'action';
is_dir($this->app_dir . 'lib') && $autoloader->autoloadPath[] = $this->app_dir . 'lib';
}

if (!$this->devMode)
{
$autoloader->storeHandle = $this->coreCacheHandle;
}
$autoloader->init();
}

protected function prepareConfig()
{
$this->configHandle = LtObjectUtil::singleton('LtConfig');
if (!$this->devMode)
{
$configFile = 'conf/conf.php';
$this->configHandle->storeHandle = $this->coreCacheHandle;
}
else
{
$configFile = 'conf/conf_dev.php';
}
$this->configHandle->init();
if ($this->app_dir && is_file($this->app_dir . $configFile))
{
$this->configHandle->loadConfigFile($this->app_dir . $configFile);
}
}

protected function runMVC()
{
$router = LtObjectUtil::singleton('LtRouter');
$router->init();
$dispatcher = LtObjectUtil::singleton('LtDispatcher');
$dispatcher->viewDir = $this->app_dir . 'view/';
$dispatcher->viewTplDir = $this->data_dir . 'templateView/';
if (!$this->devMode)
{
$dispatcher->viewTplAutoCompile = false;
}
else
{
$dispatcher->viewTplAutoCompile = true;
}
$dispatcher->dispatchAction($router->module, $router->action);
}
}

View file

@ -0,0 +1,236 @@
<?php
/**
* The Action class
*/
abstract class LtAction
{
/**
* The context object
*
* @var object
*/
public $context;

public $viewDir;
public $viewTplDir;
public $viewTplAutoCompile;

/**
* The dtd config for validator
*
* @var array
*/
protected $dtds = array();

/**
* The Access Control List
*
* @var array
*/
protected $acl;

/**
* The current user's roles
*
* @var array
*/
protected $roles = array();

/**
* A flag to indicate if subclass call LtAction::__construct()
*
* @var boolean
*/
protected $constructed = false;

/**
* The response type
*
* @var string
*/
protected $responseType = "html";

/**
* Result properties
*/
protected $code;

protected $message;

public $data;

protected $view;

protected $layout;

/**
* The constructor function, initialize the URI property
*/
public function __construct()
{
$this->constructed = true;
}

public function executeChain()
{
if (!$this->constructed)
{
//DebugHelper::debug('SUBCLASS_NOT_CALL_PARENT_CONSTRUCTOR', array('class' => $actionClassName));
trigger_error('SUBCLASS_NOT_CALL_PARENT_CONSTRUCTOR');
}
$this->afterConstruct();
$validateResult = $this->validateInput();
if (0 == $validateResult["error_total"])
{
if ($this->checkPrivilege())
{
$this->beforeExecute();
$this->execute();
}
else
{
$this->code = 403;
$this->message = "Access denied";
}
}
else
{
$this->code = 407;
$this->message = "Invalid input";
$this->data['error_messages'] = $validateResult["error_messages"];
}
$this->writeResponse();
}

/**
* Do something after subClass::__construct().
*/
protected function afterConstruct()
{

}

/**
* Validate the data from client
*
* @return array
*/
protected function validateInput()
{
$validateResult = array("error_total" => 0, "error_messages" => array());
if (!empty($this->dtds) && class_exists('LtValidator'))
{
$validator = new LtValidator;
$validator->init();
foreach ($this->dtds as $variable => $dtd)
{
$from = isset($dtd->from) ? $dtd->from : 'request';

foreach ($dtd->rules as $ruleKey => $ruleValue)
{
if ($ruleValue instanceof ConfigExpression)
{
eval('$_ruleValue = ' . $ruleValue->__toString());
$dtd->rules[$ruleKey] = $_ruleValue;
}
}
$error_messages = $validator->validate($this->context->$from($variable), $dtd);
if (!empty($error_messages))
{
$validateResult['error_total'] ++;
$validateResult['error_messages'][$variable] = $error_messages;
}
}
}
return $validateResult;
}

/**
* Check if current user have privilege to do this
*
* @return boolen
*/
protected function checkPrivilege()
{
$allow = true;
if (!empty($this->roles) && class_exists('LtRbac'))
{
$module = $this->context->uri["module"];
$action = $this->context->uri["action"];
$roles = array_merge(array("*"), $this->roles);
$rbac = new LtRbac();
$rbac->init();
$allow = $rbac->checkAcl($roles, "$module/$action");
}
return $allow;
}

/**
* Do something before subClass::execute().
*/
protected function beforeExecute()
{
}

protected function execute()
{
}

protected function writeResponse()
{
switch ($this->responseType)
{
case 'json':
echo json_encode(array("code" => $this->code,
"message" => $this->message,
"data" => $this->data
));
exit; //
break;
case 'tpl':
if (null === $this->view)
{
$this->view = new LtTemplateView;
}
$this->view->component = false; // 是否组件
$this->view->context = $this->context;
$this->view->code = $this->code;
$this->view->message = $this->message;
$this->view->data = $this->data;
$this->view->layoutDir = $this->viewDir . "layout/";
$this->view->layout = $this->layout;
$this->view->templateDir = $this->viewDir;
$this->view->compiledDir = $this->viewTplDir;
$this->view->autoCompile = $this->viewTplAutoCompile;
if (empty($this->template))
{
$this->template = $this->context->uri["module"] . "-" . $this->context->uri["action"];
}
$this->view->template = $this->template;
$this->view->render();
break;

case 'html':
case 'wml':
default:
if (null === $this->view)
{
$this->view = new LtView;
}
$this->view->context = $this->context;
$this->view->code = $this->code;
$this->view->message = $this->message;
$this->view->data = $this->data;
$this->view->layoutDir = $this->viewDir . "layout/";
$this->view->layout = $this->layout;
$this->view->templateDir = $this->viewDir;
if (empty($this->template))
{
$this->template = $this->context->uri["module"] . "-" . $this->context->uri["action"];
}
$this->view->template = $this->template;
$this->view->render();
break;
}
}
}

View file

@ -0,0 +1,134 @@
<?php
/**
* The Component class
*/
abstract class LtComponent
{
/**
* The context object
*
* @var object
*/
public $context;

public $viewDir;
public $viewTplDir;
public $viewTplAutoCompile;
/**
* A flag to indicate if subclass call LtComponent::__construct()
*
* @var boolean
*/
public $constructed = false;
/**
* The response type
*
* @var string
*/
protected $responseType = "html";

/**
* Result properties
*/
protected $code;

protected $message;

public $data;

protected $view;

protected $layout;

/**
* The constructor function
*/
public function __construct()
{
$this->constructed = true;
}
public function executeChain()
{
if (!$this->constructed)
{
//DebugHelper::debug('SUBCLASS_NOT_CALL_PARENT_CONSTRUCTOR', array('class' => $actionClassName));
}
$this->afterConstruct();
$this->beforeExecute();
$this->execute();
$this->writeResponse();
}

protected function afterConstruct()
{

}
/**
* Do something before subClass::execute().
*/
protected function beforeExecute()
{
}

protected function execute()
{
}

protected function writeResponse()
{
switch ($this->responseType)
{
case 'json':
echo json_encode(array("code" => $this->code,
"message" => $this->message,
"data" => $this->data
));
exit;
break;
case 'tpl':
if (null === $this->view)
{
$this->view = new LtTemplateView;
}
$this->view->component = true; // 是否组件
$this->view->context = $this->context;
$this->view->code = $this->code;
$this->view->message = $this->message;
$this->view->data = $this->data;
$this->view->layoutDir = $this->viewDir . "layout/";
$this->view->layout = $this->layout;
$this->view->templateDir = $this->viewDir . "component/";
$this->view->compiledDir = $this->viewTplDir . "component/";
$this->view->autoCompile = $this->viewTplAutoCompile;
if (empty($this->template))
{
$this->template = $this->context->uri["module"] . "-" . $this->context->uri["action"];
}
$this->view->template = $this->template;
$this->view->render();
break;

case 'html':
case 'wml':
default:
if (null === $this->view)
{
$this->view = new LtView;
}
$this->view->context = $this->context;
$this->view->code = $this->code;
$this->view->message = $this->message;
$this->view->data = $this->data;
$this->view->layoutDir = $this->viewDir . "layout/";
$this->view->layout = $this->layout;
$this->view->templateDir = $this->viewDir . "component/";
if (empty($this->template))
{
$this->template = $this->context->uri["module"] . "-" . $this->context->uri["action"];
}
$this->view->template = $this->template;
$this->view->render();
break;
}
}
}

View file

@ -0,0 +1,100 @@
<?php
class LtContext
{
/**
* The uri property
*
* @var array
*/
public $uri;

protected $strip;

public function __construct()
{

}

/**
* return the client input in $_SERVER['argv']
*
* @param integer $offset
* @return string
*/
public function argv($offset)
{
return isset($_SERVER['argv']) && isset($_SERVER['argv'][$offset]) ? $_SERVER['argv'][$offset] : null;
}

/**
* return the client input in $_FILES
*
* @param string $name
* @return array
*/
public function file($name)
{
return isset($_FILES[$name]) ? $_FILES[$name] : null;
}

/**
* return the client input in $_GET
*
* @param string $name
* @return string
*/
public function get($name)
{
return isset($_GET[$name]) ? $_GET[$name] : null;
}

/**
* return the client input in post global array
*
* @param string $name
* @return string
*/
public function post($name)
{
$var = filter_input(INPUT_POST, $name );

return $var;
}

/**
* return the client input in $_REQUEST
*
* @param string $name
* @return string
*/
public function request($name)
{
return isset($_REQUEST[$name]) ? $_REQUEST[$name] : null;
}

/**
* return the client input in $_SERVER
*
* @param string $name
* @return string
*/
public function server($name)
{
if ('REMOTE_ADDR' == $name)
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$clientIp = $_SERVER[$name];
}
return $clientIp;
}
else
{
return isset($_SERVER[$name]) ? $_SERVER[$name] : null;
}
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* The Dispatcher class
*/
class LtDispatcher
{
public $viewDir;
public $viewTplDir;
public $viewTplAutoCompile;
public $data;

public function __construct()
{

}

protected function _dispatch($module, $action, $context = null, $classType = "Action")
{
$classType = ucfirst($classType);
$actionClassName = $module . $action . $classType;
if (!class_exists($actionClassName))
{
//DebugHelper::debug("{$classType}_CLASS_NOT_FOUND", array(strtolower($classType) => $action));
trigger_error("{$actionClassName} CLASS NOT FOUND! module={$module} action={$action} classType={$classType}");
}
else
{
if (!($context instanceof LtContext))
{
$newContext = new LtContext;
}
else
{
$newContext = clone $context;
}
$newContext->uri['module'] = $module;
$newContext->uri[strtolower($classType)] = $action;
$actionInstance = new $actionClassName();
$actionInstance->context = $newContext;
$actionInstance->viewDir = $this->viewDir;
$actionInstance->viewTplDir = $this->viewTplDir; // 模板编译目录
$actionInstance->viewTplAutoCompile = $this->viewTplAutoCompile;
$actionInstance->executeChain();
$this->data = $actionInstance->data;
}
}

/**
* Disptach the module/action calling.
*
* @param $module string
* @param $action string
* @return void
* @todo allow one action dispatch another action
*/
public function dispatchAction($module, $action, $context = null)
{
$this->_dispatch($module, $action, $context);
}

/**
* Disptach the module/component calling.
*
* @param $module string
* @param $component string
* @param $data mixed
* @return void
*/
public function dispatchComponent($module, $component, $context = null)
{
$cloneOfContext = clone $context;
$this->_dispatch($module, $component, $cloneOfContext, "Component");
}
}

View file

@ -0,0 +1,385 @@
<?php
class LtTemplateView
{
public $layout;
public $layoutDir;

public $template;
public $templateDir;
public $compiledDir;

public $autoCompile; // bool
public $component; // bool
private $tpl_include_files;

public function __construct()
{
/**
* 自动编译通过对比文件修改时间确定是否编译,
* 当禁止自动编译时, 需要手工删除编译后的文件来重新编译.
*
* 支持component include自动编译
*/
$this->autoCompile = true;
$this->component = false;
}

public function render()
{
if (empty($this->compiledDir))
{
$this->compiledDir = dirname($this->templateDir) . "/viewTpl/";
}
if (!empty($this->layout))
{
include $this->template(true);
}
else if ($this->component)
{
return; // 模板内使用{component module action}合并文件
}
else
{
include $this->template();
}
}

/**
* 返回编译后的模板路径, 如果不存在则编译生成并返回路径.
* 如果文件存在且允许自动编译, 则对比模板文件和编译后的文件修改时间
* 当修改模板后自支重新编译
*
* @param bool $islayout 是否使用布局
* @return string 返回编译后的模板路径
*/
public function template($islayout = false)
{
$this->layoutDir = rtrim($this->layoutDir, '\\/') . '/';
$this->compiledDir = rtrim($this->compiledDir, '\\/') . '/';
$this->templateDir = rtrim($this->templateDir, '\\/') . '/';

if ($islayout)
{
$tplfile = $this->layoutDir . $this->layout . '.php';
$objfile = $this->compiledDir . 'layout/' . $this->layout . '@' . $this->template . '.php';
}
else
{
$tplfile = $this->templateDir . $this->template . '.php';
$objfile = $this->compiledDir . $this->template . '.php';
}
if (is_file($objfile))
{
if ($this->autoCompile)
{
$iscompile = true;
$tpl_include_files = include($objfile);
$last_modified_time = array();
foreach($tpl_include_files as $f)
{
$last_modified_time[] = filemtime($f);
}
if (filemtime($objfile) == max($last_modified_time))
{
$iscompile = false;
}
}
else
{
$iscompile = false;
}
}
else
{
// 目标文件不存在,编译模板
$iscompile = true;
}
if ($iscompile)
{
$this->tpl_include_files[] = $objfile;
$this->tpl_include_files[] = $tplfile;
$dir = pathinfo($objfile, PATHINFO_DIRNAME);
if (!is_dir($dir))
{
if (!mkdir($dir, 0777, true))
{
trigger_error("Can not create $dir");
}
}
$str = file_get_contents($tplfile);
if (!$str)
{
trigger_error('Template file Not found or have no access!', E_USER_ERROR);
}
$str = $this->parse($str);
if ($this->autoCompile)
{
$prefix = "<?php\r\nif(isset(\$iscompile)&&true==\$iscompile)\r\nreturn " . var_export(array_unique($this->tpl_include_files), true) . ";?>";
$prefix = preg_replace("/([\r\n])+/", "\r\n", $prefix);
$postfix = "\r\n<!--Template compilation time : " . date('Y-m-d H:i:s') . "-->\r\n";
}
else
{
$prefix = '';
$postfix = '';
}
$str = $prefix . $str . $postfix;
if (!file_put_contents($objfile, $str))
{
if (file_put_contents($objfile . '.tmp', $str))
{
copy($objfile . '.tmp', $objfile); // win下不能重命名已经存在的文件
unlink($objfile . '.tmp');
}
}
@chmod($objfile,0777);
}
return $objfile;
}

/**
* 解析{}内字符串,替换php代码
*
* @param string $str
* @return string
*/
protected function parse($str)
{
$str = $this->removeComments($str);
$str = $this->parseIncludeComponent($str);
// 回车 换行
$str = str_replace("{CR}", "<?php echo \"\\r\";?>", $str);
$str = str_replace("{LF}", "<?php echo \"\\n\";?>", $str);
// if else elseif
$str = preg_replace("/\{if\s+(.+?)\}/", "<?php if(\\1) { ?>", $str);
$str = preg_replace("/\{else\}/", "<?php } else { ?>", $str);
$str = preg_replace("/\{elseif\s+(.+?)\}/", "<?php } elseif (\\1) { ?>", $str);
$str = preg_replace("/\{\/if\}/", "<?php } ?>", $str);
// loop
$str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\}/e", "\$this->addquote('<?php if(isset(\\1) && is_array(\\1)) foreach(\\1 as \\2) { ?>')", $str);
$str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/e", "\$this->addquote('<?php if(isset(\\1) && is_array(\\1)) foreach(\\1 as \\2=>\\3) { ?>')", $str);
$str = preg_replace("/\{\/loop\}/", "<?php } ?>", $str);
// url生成
$str = preg_replace("/\{url\(([^}]+)\)\}/", "<?php echo LtObjectUtil::singleton('LtUrl')->generate(\\1);?>", $str);

// 函数
$str = preg_replace("/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\s*\(([^{}]*)\))\}/", "<?php echo \\1;?>", $str);
$str = preg_replace("/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/", "<?php echo \$\\1;?>", $str);
// 变量
/**
* 放弃支持$name.name.name
* $str = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\.([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1['\\2'];?>", $str);
*/
// 其它变量
$str = preg_replace("/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str);
$str = preg_replace("/\{(\\$[a-zA-Z0-9_\.\[\]\'\"\$\x7f-\xff]+)\}/e", "\$this->addquote('<?php echo \\1;?>')", $str);
// 类->属性 类->方法
$str = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff][+\-\>\$\'\"\,\[\]\(\)a-zA-Z0-9_\x7f-\xff]+)\}/es", "\$this->addquote('<?php echo \\1;?>')", $str);
// 常量
$str = preg_replace("/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str);
// 静态变量
$str = preg_replace("/\{([a-zA-Z0-9_]*::?\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str);
$str = preg_replace("/\{([a-zA-Z0-9_]*::?\\\$[a-zA-Z0-9_\.\[\]\'\"\$\x7f-\xff]+)\}/e", "\$this->addquote('<?php echo \\1;?>')", $str);

// 合并相邻php标记
$str = preg_replace("/\?\>\s*\<\?php[\r\n\t ]*/", "", $str);
/**
* 删除空行
* Dos和windows采用回车+换行CR/LF表示下一行,
* 而UNIX/Linux采用换行符LF表示下一行
* 苹果机(MAC OS系统)则采用回车符CR表示下一行.
* CR用符号 '\r'表示, 十进制ASCII代码是13, 十六进制代码为0x0D;
* LF使用'\n'符号表示, ASCII代码是10, 十六制为0x0A.
* 所以Windows平台上换行在文本文件中是使用 0d 0a 两个字节表示,
* 而UNIX和苹果平台上换行则是使用0a或0d一个字节表示.
*
* 这里统一替换成windows平台回车换行, 第二参数考虑 \\1 保持原有
*/
$str = preg_replace("/([\r\n])+/", "\r\n", $str);
// 删除第一行
$str = preg_replace("/^[\r\n]+/", "", $str);
// write
$str = trim($str);
return $str;
}
/**
* 变量加上单引号
* 如果是数字就不加单引号, 如果已经加上单引号或者双引号保持不变
*/
protected function addquote($var)
{
preg_match_all("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", $var, $vars);
foreach($vars[1] as $k => $v)
{
if (is_numeric($v))
{
$var = str_replace($vars[0][$k], "[$v]", $var);
}
else
{
$var = str_replace($vars[0][$k], "['$v']", $var);
}
}
return str_replace("\\\"", "\"", $var);
}

/**
* 模板中第一行可以写exit函数防止浏览
* 删除行首尾空白, html javascript css注释
*/
protected function removeComments($str, $clear = false)
{
$str = str_replace(array('<?php exit?>', '<?php exit;?>'), array('', ''), $str);
// 删除行首尾空白
$str = preg_replace("/([\r\n]+)[\t ]+/s", "\\1", $str);
$str = preg_replace("/[\t ]+([\r\n]+)/s", "\\1", $str);
// 删除 {} 前后的 html 注释 <!-- -->
$str = preg_replace("/\<\!\-\-\s*\{(.+?)\}\s*\-\-\>/s", "{\\1}", $str);
$str = preg_replace("/\<\!\-\-\s*\-\-\>/s", "", $str);
// 删除 html注释 存在 < { 就不删除
$str = preg_replace("/\<\!\-\-\s*[^\<\{]*\s*\-\-\>/s", "", $str);
if ($clear)
{
$str = $this->clear($str);
}
return $str;
}
/**
* 清除一部分 style script内的注释
* 多行注释内部存在 / 字符就不会清除
*/
protected function clear($str)
{
preg_match_all("|<script[^>]*>(.*)</script>|Usi", $str, $tvar);
foreach($tvar[0] as $k => $v)
{
// 删除单行注释
$v = preg_replace("/\/\/\s*[a-zA-Z0-9_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/", "", $v);
// 删除多行注释
$v = preg_replace("/\/\*[^\/]*\*\//s", "", $v);
$str = str_replace($tvar[0][$k], $v, $str);
}
preg_match_all("|<style[^>]*>(.*)</style>|Usi", $str, $tvar);
foreach($tvar[0] as $k => $v)
{
// 删除多行注释
$v = preg_replace("/\/\*[^\/]*\*\//s", "", $v);
$str = str_replace($tvar[0][$k], $v, $str);
}
return $str;
}
/**
*
* @todo 注意相互引用的模板嵌套会导致死循环
*/
protected function parseIncludeComponent($str)
{
$count_include_component = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar);
$count_include_component += preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar);
unset($tvar);
while ($count_include_component > 0)
{
$str = $this->parseInclude($str);
$str = $this->parseComponent($str);
$count_include_component = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar);
$count_include_component += preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar);
unset($tvar);
}
$str = $this->removeComments($str);
return $str;
}
/**
* 解析多个{include path/file}合并成一个文件
*
* @example {include 'debug_info'}
* {include 'debug_info.php'}
* {include "debug_info"}
* {include "debug_info.php"}
* {include $this->templateDir . $this->template}
*/
private function parseInclude($str)
{
$countSubTpl = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar);
while ($countSubTpl > 0)
{
foreach($tvar[1] as $k => $subfile)
{
eval("\$subfile = $subfile;");
if (is_file($subfile))
{
$findfile = $subfile;
}
else if (is_file($subfile . '.php'))
{
$findfile = $subfile . '.php';
}
else if (is_file($this->templateDir . $subfile))
{
$findfile = $this->templateDir . $subfile;
}
else if (is_file($this->templateDir . $subfile . '.php'))
{
$findfile = $this->templateDir . $subfile . '.php';
}
else
{
$findfile = '';
}
if (!empty($findfile))
{
$subTpl = file_get_contents($findfile);
$this->tpl_include_files[] = $findfile;
}
else
{
// 找不到文件
$subTpl = 'SubTemplate not found:' . $subfile;
}
$str = str_replace($tvar[0][$k], $subTpl, $str);
}
$countSubTpl = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar);
}
return $str;
}

/**
* 解析多个{component module action}合并成一个文件
*/
private function parseComponent($str)
{
$countCom = preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar);
while ($countCom > 0)
{
$i = 0;
while ($i < $countCom)
{
$comfile = $this->templateDir . "component/" . $tvar[1][$i] . '-' . $tvar[2][$i] . '.php';
if (is_file($comfile))
{
$subTpl = file_get_contents($comfile);
$this->tpl_include_files[] = $comfile;
}
else
{
$subTpl = 'SubTemplate not found:' . $comfile;
}
////////////////////////////////////////////////////////////////////////////
$module = $tvar[1][$i];
$action = $tvar[2][$i];
$subTpl = "<?php
\$dispatcher = LtObjectUtil::singleton('LtDispatcher');
\$dispatcher->dispatchComponent('$module', '$action', \$this->context);
\$comdata = \$dispatcher->data;
unset(\$dispatcher);
?>
" . $subTpl;
////////////////////////////////////////////////////////////////////////////
$str = str_replace($tvar[0][$i], $subTpl, $str);
$i++;
}
$countCom = preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar);
}
return $str;
}
}

View file

@ -0,0 +1,26 @@
<?php
/**
* The View class
*/
class LtView
{
public $layoutDir;

public $templateDir;

public $layout;

public $template;

public function render()
{
if (!empty($this->layout))
{
include($this->layoutDir . $this->layout . '.php');
}
else
{
include($this->templateDir . $this->template . '.php');
}
}
}

View file

@ -0,0 +1,33 @@
<?php
class LtObjectUtil
{
static $instances;

static public function singleton($className, $autoInited = true)
{
if (empty($className))
{
trigger_error('empty class name');
return false;
}
$key = strtolower($className);
if (isset(self::$instances[$key]))
{
return self::$instances[$key];
}
else if (class_exists($className))
{
$newInstance = new $className;
if ($autoInited && method_exists($newInstance, 'init'))
{
$newInstance->init();
}
self::$instances[$key] = $newInstance;
return $newInstance;
}
else
{
return false;
}
}
}

View file

@ -0,0 +1,226 @@
<?php
class LtPagination
{
public $configHandle;
public $conf;

public function __construct()
{
if (! $this->configHandle instanceof LtConfig)
{
if (class_exists("LtObjectUtil", false))
{
$this->configHandle = LtObjectUtil::singleton("LtConfig");
}
else
{
$this->configHandle = new LtConfig;
}
}
}

public function init()
{
$this->conf = $this->configHandle->get("pagination.pager");
if (empty($this->conf))
{
$this->conf['per_page'] = 25; //每个页面中希望展示的项目数量
$this->conf['num_links_show'] = 9; //数字链接显示数量
$this->conf['num_point_start_end'] = 2; //“点”前边和后边的链接数量
$this->conf['show_first'] = true;
$this->conf['show_prev'] = true;
$this->conf['show_next'] = true;
$this->conf['show_last'] = true;
$this->conf['show_goto'] = false;
$this->conf['show_info'] = false;
$this->conf['show_point'] = true;
$this->conf['show_empty_button'] = false;

$this->conf['first_text'] = 'First';
$this->conf['prev_text'] = 'Prev';
$this->conf['next_text'] = 'Next';
$this->conf['last_text'] = 'Last';
$this->conf['point_text'] = '...';

$this->conf['full_tag_open'] = '<div class="pages">';
$this->conf['full_tag_close'] = '</div>';
$this->conf['num_tag_open'] = '';
$this->conf['num_tag_close'] = '';
$this->conf['link_tag_open'] = '<a href=":url">';
$this->conf['link_tag_close'] = '</a>';
$this->conf['link_tag_cur_open'] = '<strong>';
$this->conf['link_tag_cur_close'] = '</strong>';
$this->conf['button_tag_open'] = '<a href=":url" style="font-weight:bold">';
$this->conf['button_tag_close'] = '</a>';
$this->conf['button_tag_empty_open'] = '<span>';
$this->conf['button_tag_empty_close'] = '</span>';
$this->conf['point_tag_open'] = '<span>';
$this->conf['point_tag_close'] = '</span>';
}
}

/**
*
* @param $page int 当前页
* @param $count int 这个数值是你查询数据库得到的数据总量
* @param $url string 字串中使用 :page 表示页参数 不在意位置 如/a/:page/c/e
*/
public function pager($page, $count, $url)
{
$per_page = empty($this->conf['per_page']) ? 25 : $this->conf['per_page'];
$pagecount = ceil($count / $per_page);
$pager = $this->renderPager($page, $pagecount, $url);
if ($this->conf['show_goto'])
{
$pager .= $this->renderButton('goto', $page, $pagecount, $url);
}
if ($this->conf['show_info'])
{
$pager .= $this->renderButton('info', $page, $pagecount, $url);
}
return $this->conf['full_tag_open'] . $pager . $this->conf['full_tag_close'];
}

/**
*
* @param $pagenumber int 当前页
* @param $pagecount int 总页数
* @return string
*/
public function renderPager($pagenumber, $pagecount, $baseurl = '?page=:page')
{
$baseurl = urldecode($baseurl);
$pager = $this->conf['num_tag_open'];

$pager .= $this->renderButton('first', $pagenumber, $pagecount, $baseurl);
$pager .= $this->renderButton('prev', $pagenumber, $pagecount, $baseurl);

$startPoint = 1;
$endPoint = $this->conf['num_links_show'];
$num_links = ceil($this->conf['num_links_show'] / 2) - 1;
if ($pagenumber > $num_links)
{
$startPoint = $pagenumber - $num_links;
$endPoint = $pagenumber + $num_links;
}

if ($endPoint > $pagecount)
{
$startPoint = $pagecount + 1 - $this->conf['num_links_show'];
$endPoint = $pagecount;
}

if ($startPoint < 1)
{
$startPoint = 1;
}

$currentButton = '';
if ($this->conf['show_point'])
{
for($page = 1; $page < $startPoint; $page++)
{
$url = str_replace(':page', $page, $baseurl);
if ($page > $this->conf['num_point_start_end'])
{
$currentButton .= $this->conf['point_tag_open'] . $this->conf['point_text'] . $this->conf['point_tag_close'];
break;
}
$currentButton .= str_replace(':url', $url, $this->conf['link_tag_open']) . $page . $this->conf['link_tag_close'];
}
}
for ($page = $startPoint; $page <= $endPoint; $page++)
{
$url = str_replace(':page', $page, $baseurl);
if ($page == $pagenumber)
{
$currentButton .= $this->conf['link_tag_cur_open'] . $page . $this->conf['link_tag_cur_close'];
}
else
{
$currentButton .= str_replace(':url', $url, $this->conf['link_tag_open']) . $page . $this->conf['link_tag_close'];
}
}
if ($this->conf['show_point'])
{
$page = $pagecount - $this->conf['num_point_start_end'];
if ($page > $endPoint)
{
$currentButton .= $this->conf['point_tag_open'] . $this->conf['point_text'] . $this->conf['point_tag_close'];
}

for($page += 1; $page >= $endPoint && $page <= $pagecount; $page++)
{
if ($page == $endPoint) continue;
$url = str_replace(':page', $page, $baseurl);
$currentButton .= str_replace(':url', $url, $this->conf['link_tag_open']) . $page . $this->conf['link_tag_close'];
}
}
$pager .= $currentButton;
$pager .= $this->renderButton('next', $pagenumber, $pagecount, $baseurl);
$pager .= $this->renderButton('last', $pagenumber, $pagecount, $baseurl);
$pager .= $this->conf['num_tag_close'];

return $pager;
}

/**
*
* @param $buttonLabel string 显示文字
* @param $pagenumber int 当前页
* @param $pagecount int 总页数
* @return string
*/
public function renderButton($buttonLabel, $pagenumber, $pagecount, $baseurl = '?page=:page')
{
$baseurl = urldecode($baseurl);
$destPage = 1;
if ('goto' == $buttonLabel)
{
$button = "goto <input type=\"text\" size=\"3\" onkeydown=\"javascript: if(event.keyCode==13){ location='{$baseurl}'.replace(':page',this.value);return false;}\" />";
return $button;
}
if ('info' == $buttonLabel)
{
$button = " $pagenumber/$pagecount ";
return $button;
}
switch ($buttonLabel)
{
case "first":
$destPage = 1;
$bottenText = $this->conf['first_text'];
break;
case "prev":
$destPage = $pagenumber - 1;
$bottenText = $this->conf['prev_text'];
break;
case "next":
$destPage = $pagenumber + 1;
$bottenText = $this->conf['next_text'];
break;
case "last":
$destPage = $pagecount;
$bottenText = $this->conf['last_text'];
break;
}
$url = str_replace(':page', $destPage, $baseurl);
$button = str_replace(':url', $url, $this->conf['button_tag_open']) . $bottenText . $this->conf['button_tag_close'];

if ($buttonLabel == "first" || $buttonLabel == "prev")
{
if ($pagenumber <= 1)
{
$button = $this->conf['show_empty_button'] ? $this->conf['button_tag_empty_open'] . $bottenText . $this->conf['button_tag_empty_close'] : '';
}
}
else
{
if ($pagenumber >= $pagecount)
{
$button = $this->conf['show_empty_button'] ? $this->conf['button_tag_empty_open'] . $bottenText . $this->conf['button_tag_empty_close'] : '';
}
}
return $button;
}
}

View file

@ -0,0 +1,87 @@
<?php
class LtRbac {

public $configHandle;

protected $acl;

public function __construct()
{
if (! $this->configHandle instanceof LtConfig)
{
if (class_exists("LtObjectUtil", false))
{
$this->configHandle = LtObjectUtil::singleton("LtConfig");
}
else
{
$this->configHandle = new LtConfig;
}
}
}

public function init()
{
$this->acl = $this->configHandle->get('rbac.acl');
}

public function checkAcl($roles, $resource)
{
$allow = false;
// deny priority
foreach (array("allow", "deny") as $operation)
{
foreach($roles as $role)
{
if (isset($this->acl[$operation][$role]))
{
// everyone *
if (in_array($resource, $this->acl[$operation]['*']))
{
$allow = "allow" == $operation ? true : false;
break;
}
if (in_array($resource, $this->acl[$operation][$role]))
{
$allow = "allow" == $operation ? true : false;
break;
}
else
{
$res = explode('/', trim($resource, '/'));
for ($i = count($res)-1; $i >= 0; $i--)
{
$res[$i] = '*';
$tmp = implode('/', $res);
if (in_array($tmp, $this->acl[$operation][$role]))
{
$allow = "allow" == $operation ? true : false;
break;
}
unset($res[$i]);
}
}
}
}
}
return $allow;
}
/*
private function __set($p,$v)
{
$this->$p = $v;
}

private function __get($p)
{
if(isset($this->$p))
{
return($this->$p);
}
else
{
return(NULL);
}
}
*/
}

View file

@ -0,0 +1,194 @@
<?php
/**
* The Router class
*/
class LtRouter
{
public $configHandle;
public $routingTable;
public $module;
public $action;

public function __construct()
{
if (! $this->configHandle instanceof LtConfig)
{
if (class_exists("LtObjectUtil"))
{
$this->configHandle = LtObjectUtil::singleton("LtConfig");
}
else
{
$this->configHandle = new LtConfig;
}
}
}

public function init()
{
$this->routingTable = $this->configHandle->get("router.routing_table");
if (empty($this->routingTable))
{
$this->routingTable = array('pattern' => ":module/:action/*",
'default' => array('module' => 'default', 'action' => 'index'),
'reqs' => array('module' => '[a-zA-Z0-9\.\-_]+',
'action' => '[a-zA-Z0-9\.\-_]+'
),
'varprefix' => ':',
'delimiter' => '/',
'postfix' => '',
'protocol' => 'PATH_INFO', // REWRITE STANDARD
);
}

$delimiter = $this->routingTable['delimiter'];
$postfix = $this->routingTable['postfix'];
$protocol = strtoupper($this->routingTable['protocol']);
$module = '';
$action = '';
$params = array();
// HTTP HTTPS
if (isset($_SERVER['SERVER_PROTOCOL']))
{
if (isset($_SERVER['PATH_INFO']) && !empty($_SERVER['PATH_INFO']))
{
// 忽略后缀
$url = rtrim($_SERVER['PATH_INFO'], "$postfix");
$url = explode($delimiter, trim($url, "/"));
}
else if (isset($_SERVER['REQUEST_URI']))
{
if ('REWRITE' == $protocol)
{
if (0 == strcmp($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']))
{
$url = array();
}
else
{
$url = substr($_SERVER['REQUEST_URI'], strlen(pathinfo($_SERVER['SCRIPT_NAME'], PATHINFO_DIRNAME)));
$url = rtrim($url, "$postfix");
$url = explode($delimiter, trim($url, "/"));
}
}
else if ('PATH_INFO' == $protocol)
{
$url = substr($_SERVER['REQUEST_URI'], strlen($_SERVER['SCRIPT_NAME']));
$url = rtrim($url, "$postfix");
$url = explode($delimiter, trim($url, "/"));
}
else //STANDARD
{
$url = array();
foreach($_GET as $v)
{
$url[] = $v;
}
}
}
else
{
$url = array();
foreach($_GET as $v)
{
$url[] = $v;
}
}
$params = $this->matchingRoutingTable($url);
$module = $params['module'];
$action = $params['action'];
}
else
{
// CLI
$i = 0;
while (isset($_SERVER['argv'][$i]) && isset($_SERVER['argv'][$i + 1]))
{
if (("-m" == $_SERVER['argv'][$i] || "--module" == $_SERVER['argv'][$i]))
{
$module = $_SERVER['argv'][$i + 1];
}
else if (("-a" == $_SERVER['argv'][$i] || "--action" == $_SERVER['argv'][$i]))
{
$action = $_SERVER['argv'][$i + 1];
}
else
{
$key = $_SERVER['argv'][$i];
$params[$key] = $_SERVER['argv'][$i + 1];
}
$i = $i + 2;
}
}
// 如果$_GET中不存在配置的变量则添加
foreach($params as $k => $v)
{
!isset($_GET[$k]) && $_GET[$k] = $v;
}
$this->module = $module;
$this->action = $action;
}

/**
* url 匹配路由表
*
* @param $ [string|array] $url
* @return
* @todo 修复导致$_GET多出属性的BUG
* @todo 如果是rewrite或者path_info模式可能需要unset module和action两个$_GET变量
*/
public function matchingRoutingTable($url)
{
$ret = $this->routingTable['default']; //初始化返回值为路由默认值
$reqs = $this->routingTable['reqs'];
$delimiter = $this->routingTable['delimiter'];
$varprefix = $this->routingTable['varprefix'];
$postfix = $this->routingTable['postfix'];
$pattern = explode($delimiter, trim($this->routingTable['pattern'], $delimiter));

/**
* 预处理url
*/
if (is_string($url))
{
$url = rtrim($url, $postfix); //忽略后缀
$url = explode($delimiter, trim($url, $delimiter));
}

foreach($pattern as $k => $v)
{
if ($v[0] == $varprefix)
{
// 变量
$varname = substr($v, 1);
// 匹配变量
if (isset($url[$k]))
{
if (isset($reqs[$varname]))
{
$regex = "/^{$reqs[$varname]}\$/i";
if (preg_match($regex, $url[$k]))
{
$ret[$varname] = $url[$k];
}
}
}
}
else if ($v[0] == '*')
{
// 通配符
$pos = $k;
while (isset($url[$pos]) && isset($url[$pos + 1]))
{
$ret[$url[$pos ++]] = urldecode($url[$pos]);
$pos++;
}
}
else
{
// 静态
}
}
return $ret;
}
}

View file

@ -0,0 +1,56 @@
<?php
class LtSession
{
public $storeHandle;
public $configHandle;

public function __construct()
{
if (! $this->configHandle instanceof LtConfig)
{
if (class_exists("LtObjectUtil", false))
{
$this->configHandle = LtObjectUtil::singleton("LtConfig");
}
else
{
$this->configHandle = new LtConfig;
}
}
}

public function init()
{
if(!$sessionSavePath = $this->configHandle->get("session.save_path"))
{
$sessionSavePath = '/tmp/Lotus/session/';
}
if (!is_object($this->storeHandle))
{
ini_set('session.save_handler', 'files');
if (!is_dir($sessionSavePath))
{
if (!@mkdir($sessionSavePath, 0777, true))
{
trigger_error("Can not create $sessionSavePath");
}
}
session_save_path($sessionSavePath);
}
else
{
$this->storeHandle->conf = $this->configHandle->get("session.conf");
$this->storeHandle->init();
session_set_save_handler(
array(&$this->storeHandle, 'open'),
array(&$this->storeHandle, 'close'),
array(&$this->storeHandle, 'read'),
array(&$this->storeHandle, 'write'),
array(&$this->storeHandle, 'destroy'),
array(&$this->storeHandle, 'gc')
);
}
//session_start();
//header("Cache-control: private"); // to overcome/fix a bug in IE 6.x
}
}

View file

@ -0,0 +1,10 @@
<?php
Interface LtSessionStore
{
public function open($save_path, $name);
public function close();
public function read($id);
public function write($id, $data);
public function destroy($id);
public function gc($maxlifetime=0);
}

View file

@ -0,0 +1 @@
<?php

View file

@ -0,0 +1 @@
<?php

View file

@ -0,0 +1 @@
<?php

View file

@ -0,0 +1,142 @@
<?php
class LtSessionSqlite implements LtSessionStore
{
public $conf;

private $lifeTime; //session.gc_maxlifetime
private $dbHandle;
private $dbName;
private $tableName;

public function __construct()
{
// --
}
public function init()
{
if (isset($this->conf['gc_maxlifetime']))
{
$this->lifeTime = $this->conf['gc_maxlifetime'];
}
else
{
$this->lifeTime = get_cfg_var("session.gc_maxlifetime");
}
if (isset($this->conf['table_name']))
{
$this->tableName = $this->conf['table_name'];
}
else
{
$this->tableName = 'lotus_session';
}
if (isset($this->conf['db_name']))
{
$this->dbName = $this->conf['db_name'];
}
else
{
$this->dbName = '/tmp/Lotus/session/session_sqlite2.db';
}

if (!$this->dbHandle = sqlite_open($this->dbName, 0666))
{
trigger_error('session sqlite db error');
return false;
}
return true;
}

public function open($savePath, $sessName)
{
return true;
}

public function close()
{
$this->gc($this->lifeTime);
return @sqlite_close($this->dbHandle);
}

public function read($sessID)
{
$res = sqlite_query("SELECT session_data AS d FROM $this->tableName
WHERE session_id = '$sessID'
AND session_expires > " . time(), $this->dbHandle);
if ($row = sqlite_fetch_array($res, SQLITE_ASSOC))
{
return $row['d'];
}
else
{
return "";
}
}

public function write($sessID, $sessData)
{
$newExp = time() + $this->lifeTime;
$res = sqlite_query("SELECT * FROM $this->tableName
WHERE session_id = '$sessID'", $this->dbHandle);
if (sqlite_num_rows($res))
{
sqlite_exec("UPDATE $this->tableName
SET session_expires = '$newExp',
session_data = '$sessData'
WHERE session_id = '$sessID'", $this->dbHandle);
if (sqlite_changes($this->dbHandle))
{
return true;
}
}
else
{
sqlite_exec("INSERT INTO $this->tableName (
session_id,
session_expires,
session_data)
VALUES(
'$sessID',
'$newExp',
'$sessData')", $this->dbHandle);
if (sqlite_changes($this->dbHandle))
{
return true;
}
}
return false;
}

public function destroy($sessID)
{
sqlite_exec("DELETE FROM $this->tableName WHERE session_id = '$sessID'", $this->dbHandle);
if (sqlite_changes($this->dbHandle))
{
return true;
}
return false;
}

public function gc($sessMaxLifeTime)
{
sqlite_exec("DELETE FROM $this->tableName WHERE session_expires < " . time(), $this->dbHandle);
return sqlite_changes($this->dbHandle);
}

public function runOnce()
{
$sql = "SELECT name FROM sqlite_master WHERE type='table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='table' AND name='" . $this->tableName . "'";
$res = sqlite_query($sql, $this->dbHandle);
$row = sqlite_fetch_array($res, SQLITE_ASSOC);
if (empty($row))
{
$sql = "CREATE TABLE $this->tableName (
[session_id] VARCHAR(255) NOT NULL PRIMARY KEY,
[session_expires] INTEGER DEFAULT '0' NOT NULL,
[session_data] TEXT NULL
)";
return sqlite_exec($sql, $this->dbHandle);
}
return false;
}
}

View file

@ -0,0 +1,8 @@
<?php
Interface LtStore
{
public function add($key, $value);
public function del($key);
public function get($key);
public function update($key, $value);
}

View file

@ -0,0 +1,125 @@
<?php
class LtStoreFile implements LtStore
{
public $storeDir;
public $prefix = 'LtStore';
public $useSerialize = false;
static public $defaultStoreDir = "/tmp/LtStoreFile/";
public function init()
{
/**
* 目录不存在和是否可写在调用add是测试
* @todo detect dir is exists and writable
*/
if (null == $this->storeDir)
{
$this->storeDir = self::$defaultStoreDir;
}
$this->storeDir = str_replace('\\', '/', $this->storeDir);
$this->storeDir = rtrim($this->storeDir, '\\/') . '/';
}

/**
* 当key存在时:
* 如果没有过期, 不更新值, 返回 false
* 如果已经过期, 更新值, 返回 true
*
* @return bool
*/
public function add($key, $value)
{
$file = $this->getFilePath($key);
$cachePath = pathinfo($file, PATHINFO_DIRNAME);
if (!is_dir($cachePath))
{
if (!@mkdir($cachePath, 0777, true))
{
trigger_error("Can not create $cachePath");
}
}
if (is_file($file))
{
return false;
}
if ($this->useSerialize)
{
$value = serialize($value);
}
$length = file_put_contents($file, '<?php exit;?>' . $value);
return $length > 0 ? true : false;
}

/**
* 删除不存在的key返回false
*
* @return bool
*/
public function del($key)
{
$file = $this->getFilePath($key);
if (!is_file($file))
{
return false;
}
else
{
return @unlink($file);
}
}

/**
* 取不存在的key返回false
* 已经过期返回false
*
* @return 成功返回数据,失败返回false
*/
public function get($key)
{
$file = $this->getFilePath($key);
if (!is_file($file))
{
return false;
}
$str = file_get_contents($file);
$value = substr($str, 13);
if ($this->useSerialize)
{
$value = unserialize($value);
}
return $value;
}

/**
* key不存在 返回false
* 不管有没有过期,都更新数据
*
* @return bool
*/
public function update($key, $value)
{
$file = $this->getFilePath($key);
if (!is_file($file))
{
return false;
}
else
{
if ($this->useSerialize)
{
$value = serialize($value);
}
$length = file_put_contents($file, '<?php exit;?>' . $value);
return $length > 0 ? true : false;
}
}

public function getFilePath($key)
{
$token = md5($key);
return $this->storeDir .
$this->prefix . '/' .
substr($token, 0, 2) .'/' .
substr($token, 2, 2) . '/' .
$token . '.php';
}
}

View file

@ -0,0 +1,54 @@
<?php
class LtStoreMemory implements LtStore
{
protected $stack;

public function add($key, $value)
{
if (isset($this->stack[$key]))
{
return false;
}
else
{
$this->stack[$key] = $value;
return true;
}
}

public function del($key)
{
if (isset($this->stack[$key]))
{
unset($this->stack[$key]);
return true;
}
else
{
return false;
}
}

public function get($key)
{
return isset($this->stack[$key]) ? $this->stack[$key] : false;
}

/**
* key不存在返回false
*
* @return bool
*/
public function update($key, $value)
{
if (!isset($this->stack[$key]))
{
return false;
}
else
{
$this->stack[$key] = $value;
return true;
}
}
}

Some files were not shown because too many files have changed in this diff Show more