作者 李小龙

C端客户源码版项目

要显示太多修改。

为保证性能只显示 40 of 40+ 个文件。

root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4
... ...
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=forget
DB_USERNAME=forget
DB_PASSWORD=forget
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
... ...
* text=auto
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
... ...
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
... ...
php:
preset: laravel
version: 8
disabled:
- no_unused_imports
finder:
not-name:
- index.php
- server.php
js:
finder:
not-name:
- webpack.mix.js
css: true
... ...
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
\ No newline at end of file
... ...
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400"></a></p>
<p align="center">
<a href="https://travis-ci.org/laravel/framework"><img src="https://travis-ci.org/laravel/framework.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
## About Laravel
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
Laravel is accessible, powerful, and provides tools required for large, robust applications.
## Learning Laravel
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 1500 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
## Laravel Sponsors
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
### Premium Partners
- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Cubet Techno Labs](https://cubettech.com)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[Many](https://www.many.co.uk)**
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
- **[DevSquad](https://devsquad.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
- **[OP.GG](https://op.gg)**
- **[WebReinvent](https://webreinvent.com/?utm_source=laravel&utm_medium=github&utm_campaign=patreon-sponsors)**
- **[Lendio](https://lendio.com)**
## Contributing
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
## License
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
... ...
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
... ...
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
}
... ...
<?php
namespace App\Exceptions;
use Exception;
/**
*
* Class TipException
* @package App\Exceptions
* @author zbj
* @date 2023/12/12
*/
class TipException extends Exception
{
}
... ...
<?php
namespace App\Helper;
class Str extends \Illuminate\Support\Str
{
}
... ...
<?php
namespace App\Helper;
use Illuminate\Support\Facades\Http;
class Translate
{
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2023/10/25
* Time: 14:38
*/
namespace App\Http\Controllers\Api;
use App\Console\Commands\CosService;
use App\Exceptions\TipException;
use App\Http\Controllers\Controller;
use App\Models\Project\Project;
use App\Models\SyncSubmitTask\SyncSubmitTask;
use App\Models\WebSetting\WebSettingFormBack;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
/**
* Class InquiryController
* @package App\Http\Controllers\Api
*/
class InquiryController extends Controller
{
/**
* C端表单提交 询盘信息
* @param Request $request
* @return mixed
*/
public function inquiry(Request $request)
{
$data = $request->all();
$black_ips = ['203.86.233.27', '37.19.221.202'];
if(in_array(request()->getClientIp(), $black_ips)){
$this->responseA();
}
@file_put_contents(storage_path('logs/form_submit_' . date('Y-m-d') . '.log'), var_export(date('Y-m-d H:i:s') . "-表单提交数据:" . json_encode($data), true) . PHP_EOL, FILE_APPEND);
//同一ip 5秒内超过5次 限制1小时
$ip = $request->ip();
$lock_key = 'lock_ip_' . $ip;
$rate_key = 'rate_limit:' . $ip;
if(Cache::has($lock_key)){
$this->responseA();
}
if (Cache::has($rate_key)) {
$count = Cache::get($rate_key);
if ($count >= 5) {
Cache::put($lock_key, 1, 3600);
$this->responseA();
}
Cache::increment($rate_key);
} else {
Cache::put($rate_key, 1, 5);
}
try {
$files = $request->allFiles();
foreach ($files as $key => $file) {
$cos = new CosService();
$fileinfo = $cos->checkFile($file);
$fileName = uniqid().rand(10000,99999).'.'.$file->getClientOriginalExtension();
$path = $cos->uploadFile($file, '/inquiry/'. date('Ymd'), $fileName);
$data[$key] = [
'path' => $path,
'original_name' => $fileinfo['name'],
];
}
}catch (TipException $e){
$this->responseA([], 400, $e->getMessage());
}catch (\Exception $e){
$this->responseA([], 400, 'File upload failed');
}
//异步处理
if(!SyncSubmitTask::addTask(SyncSubmitTask::TYPE_INQUIRY, $data)){
$this->responseA([], 400, 'error');
}
$this->responseA($this->inquiryResult());
}
/**
* 收集邮箱或者手机号等其他信息
* @param Request $request
* @return mixed
*/
public function inquiryOtherInfo(Request $request)
{
return $this->inquiry($request);
}
/**
* 起点表单询盘
* @param Request $request
* @return mixed
* @author zbj
* @date 2023/12/8
*/
public function inquiryQd(Request $request){
$data = $request->post();
@file_put_contents(storage_path('logs/form_submit_' . date('Y-m-d') . '.log'), var_export(date('Y-m-d H:i:s') . "-起点表单提交数据:" . json_encode($data), true) . PHP_EOL, FILE_APPEND);
//Name,Email,Phone,Message,submit_ip,submit_time,refer
$submit_ip = $data['submit_ip'] ?? '';
$submit_time = $data['submit_time'] ?? '';
$refer = $data['refer'] ?? '';
unset($data['submit_ip']);
unset($data['submit_time']);
unset($data['refer']);
//异步处理
if(!SyncSubmitTask::addTask(SyncSubmitTask::TYPE_INQUIRY, $data, $submit_ip, $refer, $submit_time)){
$this->responseA([], 400, 'error');
}
$this->responseA($this->inquiryResult());
}
/**
* @return mixed|string[]
* @author zbj
* @date 2023/12/4
*/
protected function inquiryResult(){
$domain = request()->getHost();
$cache_key = 'inquiry_form_back_' . $domain;
$result = Cache::get($cache_key);
if(!$result){
$result = [
'message' => "",
'url' => ""
];
$projectDomain = Project::getProjectByDomain($domain);
$webFormBack = WebSettingFormBack::where("project_id", $projectDomain['project_id']??0)->first();
if (!empty($webFormBack)) {
$result["message"] = $webFormBack->message ?? "";
$result["url"] = $webFormBack->url ?? "";
Cache::put($cache_key, $result, 3600);
}
}
return $result;
}
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2023/09/02
* Time: 10:36
*/
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use ZipArchive;
/**
* Class NoticeController
* @package App\Http\Controllers\Api
*/
class NoticeController extends Controller
{
const SUCCESS = 200;
const ERROR = 400;
const SERVERERROR = 500;
protected $header = [];//设置请求头参数
/**
* @param array $data
* @param string $message
* @param int $status
* @return string
*/
protected function success($data = [], $message = 'success', $status = 200)
{
$array = compact('status', 'message', 'data');
return json_encode($array, JSON_UNESCAPED_UNICODE);
}
/**
* @param int $status
* @param string $message
* @param array $data
* @return string
*/
protected function error($message = 'error', $status = 400, $data = [])
{
$array = compact('status', 'message', 'data');
return json_encode($array, JSON_UNESCAPED_UNICODE);
}
/**
* 响应
* @param null $msg
* @param string $code
* @param array $data
* @param int $result_code
* @param string $type
* @return JsonResponse
*/
public function response($msg = null,string $code = self::SUCCESS,$data = [],$result_code = 200,$type = 'application/json'): JsonResponse
{
$result = [
'msg' => $msg,
'code' => $code,
'data' => $data,
];
$this->header['Content-Type'] = $type;
$response = response($result,$result_code,$this->header);
throw new HttpResponseException($response);
}
/**
* GET请求
* @param $url
* @return mixed
*/
public function curlGet($url){
$ch1 = curl_init();
$timeout = 0;
curl_setopt($ch1, CURLOPT_URL, $url);
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch1, CURLOPT_ENCODING, '');
curl_setopt($ch1, CURLOPT_MAXREDIRS, 10);
curl_setopt($ch1, CURLOPT_HTTPHEADER, array());
curl_setopt($ch1, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch1, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch1, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch1, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch1, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
$access_txt = curl_exec($ch1);
curl_close($ch1);
return json_decode($access_txt, true);
}
/**
* A端上传验证代码
*/
public function uploadVerifyFile(Request $request): string
{
$domain = $request->getHost();
$files = $request->allFiles();
foreach ($files as $file) {
$destinationPath = public_path($domain);
$file->move($destinationPath, $file->getClientOriginalName());
$filePath = $destinationPath . '/' . $file->getClientOriginalName();
@file_put_contents(storage_path('logs/upload_file.log'), date('Y-m-d H:i:s') . " ".$filePath . PHP_EOL, FILE_APPEND);
}
return $this->success();
}
/**
* A端上传amp站验证代码
* @param Request $request
* @return string
*/
public function uploadAmpVerifyFile(Request $request): string
{
$domain = $request->getHost();
$domain_array = parse_url($domain);
$host = $domain_array['host'] ?? $domain_array['path'];
$host_array = explode('.',$host);
if(count($host_array) <= 2){
array_unshift($host_array,'m');
}else{
$host_array[0] = 'm';
}
$amp_domain = implode('.',$host_array);
$files = $request->allFiles();
foreach ($files as $file) {
$destinationPath = public_path($amp_domain);
$file->move($destinationPath, $file->getClientOriginalName());
$filePath = $destinationPath . '/' . $file->getClientOriginalName();
@file_put_contents(storage_path('logs/upload_file.log'), date('Y-m-d H:i:s') . " ".$filePath . PHP_EOL, FILE_APPEND);
}
return $this->success();
}
/**
* 获取需要下载的文件url
* @param Request $request
* @return string
*/
public function websiteHtml(Request $request){
// $domain = $request->getHost();
//临时测试域名
$domain = "v6-1x28w.globalso.site";
$token = env("WEB_SITE_TOKEN");
$apiUrl = env("API_URL");
$requestUrl = $apiUrl."?domain=".$domain."&token=".$token;
try {
$res = $this->curlGet($requestUrl);
$url = isset($res["data"]["url"]) && !empty($res["data"]["url"]) ? urldecode($res["data"]["url"]) : "";
if ($res["status"] != self::SUCCESS || $url == ""){
return $this->error('请求失败!');
}
} catch (\Exception $e) {
return $this->error('请求失败!');
}
return $this->websiteHtmlHandle($url);
}
/**
* 网站html解压
* @param $url
* @return string
*/
public function websiteHtmlHandle($url)
{
$pathInfo = pathinfo($url);
$extension = $pathInfo['extension'];
//只允许解压zip格式文件
if (in_array($extension, ["zip"])) {
try {
$targetFile = $this->downLoadFile($url);
$zip = new ZipArchive();
if ($zip->open($targetFile) === TRUE) {
$outputFolder = public_path();
if (!is_dir($outputFolder)) {
mkdir($outputFolder, 0777, true);
}
// 解压缩文件,保留原文件结构
$zip->extractTo($outputFolder);
$zip->close();
$this->deleteDirectory($targetFile);
} else {
return $this->error('解压失败!');
// 处理打开压缩文件失败的情况
}
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}else{
return $this->error('不允许解压改格式压缩包!');
}
return $this->success();
}
/**
* 下载文件(下载到public目录下)
* @param $url
* @return string
*/
public function downLoadFile($url){
$savePath = public_path();
if (!file_exists($savePath)) {
mkdir($savePath, 0777, true);
}
$targetFile = $savePath . '/' . basename($url);
$context = stream_context_create([
'http' => [
'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
],
]);
$fileContent = @file_get_contents($url, false, $context);
if ($fileContent !== false) {
file_put_contents($targetFile, $fileContent);
return $targetFile;
} else {
return "";
}
}
/**
* 删除文件
* @param $path
* @return bool
*/
public function deleteDirectory($path): bool
{
if (!is_dir($path)) {
try {
unlink($path);
} catch (\Exception $e) {
return true;
}
return true;
}
$files = array_diff(scandir($path), array('.', '..'));
foreach ($files as $file) {
$filePath = $path . '/' . $file;
if (is_dir($filePath)) {
$this->deleteDirectory($filePath);
} else {
try {
unlink($filePath);
} catch (\Exception $e) {
return true;
}
}
}
rmdir($path);
return true;
}
/**
* 搜索
* @param Request $request
*/
public function search(Request $request)
{
$project = $request->get('project');
$data = $request->all();
//获取搜索参数
$searchContent = '';
if (isset($data['s'])) {
$searchContent = $data['s'];
}
if (isset($data['search'])) {
$searchContent = $data['search'];
}
$page = 1;
if (isset($data['page']) && (int)$data['page'] > 1) {
$page = (int)$data['page'];
}
// $htmlService = new HtmlService();
// return $htmlService->getSearchHtml($project, $data, $searchContent, $page);
}
}
... ...
<?php
namespace App\Http\Controllers\Api;
use App\Helper\Str;
use App\Http\Controllers\Controller;
use App\Models\Product\Product;
use App\Models\Product\ProductExtendInfo;
use App\Services\PageService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
/***
* 快捷搜索
* Class SearchController
* @package App\Http\Controllers\Api
* @author zbj
* @date 2024/1/22
*/
class SearchController extends Controller
{
/**
* C端搜索框快捷搜索
* @param Request $request
* @return mixed
*/
public function index(Request $request)
{
$limit = $request->input('limit') ?: 5;
$text = $request->input('text');
$key = $request->input('key') ?: 'title';
$key_limit = $request->input('key_limit') ?: 15;
$project = $request->get('project');
if (!$text) {
$this->responseA([]);
}
//匹配产品标题及产品对应的分类
$data = $this->searchProduct($text, $limit, $project->id);
//匹配建议字段
$data['suggestions'] = $this->searchSuggestion($text, $key, $key_limit, $project->id);
$this->responseA($data);
}
/**
* @param $text
* @param $limit
* @return array
* @author zbj
* @date 2024/1/22
*/
protected function searchProduct($text, $limit, $project_id = 0): array
{
//匹配产品
$all_products = Cache::get('product_list_project_' . $project_id);
if($all_products){
$products = [];
foreach ($all_products as $product){
if(Str::contains($product['title'], $text)){
$products[] = [
'title' => $product['title'],
'thumb' => $product['thumb'],
'id' => $product['id'],
'route' => $product['route'],
'category' => $product['category'],
];
if(count($products) >= $limit){
break;
}
}
}
}else{
$products = Product::with('category')->where("title", 'like', $text . '%')
->where("status", 1)
->orderBy("id", "DESC")
->limit($limit)
->select('title', 'thumb', 'id', 'route')
->get()
->toArray();
}
//对应分类
$categories = [];
foreach ($products as &$product) {
foreach ($product['category'] as $category) {
$categories[$category['route']] = [
'title' => $category['title'],
'route' => '/' . $category['route'] . '/',
];
}
unset($product['id']);
unset($product['category']);
$product['thumb'] = app(PageService::class)->getImageUrl(json_decode($product['thumb'], true)['url'] ?? '');
$product['route'] = '/' . $product['route'] . '/';
}
return [
'products' => $products,
'categories' => array_values($categories),
];
}
/**
* @param $text
* @param $key
* @param $key_limit
* @return array
* @author zbj
* @date 2024/1/22
*/
protected function searchSuggestion($text, $key, $key_limit, $project_id = 0): array
{
$columns = Cache::get('product_filed_project_' . $project_id);
if(!$columns){
$model = new Product();
$columns = $model->getConnection()->getSchemaBuilder()->getColumnListing($model->getTable());
}
//产品字段
if (in_array($key, $columns)) {
$all_products = Cache::get('product_list_project_' . $project_id);
if($all_products){
$suggestions = [];
foreach ($all_products as $product){
if(Str::contains($product[$key], $text)){
$suggestions[] = [
'title' => $product[$key],
'route' => $product['route'],
];
}
if(count($suggestions) >= $key_limit){
break;
}
}
}else{
//匹配产品
$suggestions = Product::where("status", 1)
->where($key, 'like', $text . '%')
->orderBy("id", "DESC")
->limit($key_limit)
->select($key .' as title', 'route')
->get()
->toArray();
}
} else {
$all_suggestions = Cache::get('product_extend_project_' . $project_id);
if($all_suggestions){
$suggestions = [];
foreach ($all_suggestions as $suggestion){
if(Str::contains($suggestion['values'], $text)){
$suggestions[] = [
'values' => $suggestion['values'],
'route' => $suggestion['route'],
];
}
if(count($suggestions) >= $key_limit){
break;
}
}
}else{
//扩展字段
$suggestions = Product::leftJoin('gl_product_extend_info as pei', 'gl_product.id', '=', 'pei.product_id')
->where('pei.values', 'like', $text . '%')
->where("gl_product.status", 1)
->orderBy("gl_product.id", "DESC")
->limit($key_limit)
->select('pei.values','gl_product.route')
->get()
->toArray();
}
}
foreach ($suggestions as &$suggestion){
$suggestion['route'] = '/' . $suggestion['route'] . '/';
}
return $suggestions;
}
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2024/1/6
* Time: 16:06
*/
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Repositories\SyncSubmitRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
/**
* Class TrafficController
* @package App\Http\Controllers\Api
*/
class TrafficController extends Controller
{
/**
* 特殊访问引流接口
* @param Request $request
* @return string
*/
public function trafficVisit(Request $request)
{
//获取参数
$data = $request->all();
$ip = $request->input('ip');
$url = $request->input('url');
$device_port = intval($request->input('device_port'));
$referrer_url = $request->input('referrer_url');
$user_agent = $request->input('user_agent');
// FIXME 些日志 观察数据, 完成以后删除代码
file_put_contents(storage_path('logs/trafficVisit' . date('Y-m-d') . '.log'), var_export($data, true));
//验证参数
$validator = Validator::make($data, [
'ip' => 'required',
'url' => 'required',
'user_agent' => 'required',
], [
'ip.required' => 'IP不能为空',
'url.required' => '访问链接不能为空',
'user_agent.required' => '请求头信息user_agent不能为空',
]);
//判断参数是否正确
if ($validator->fails()) {
return $this->error($validator->errors()->first());
}
$result = app(SyncSubmitRepository::class)->trafficVisit($ip, $url, $user_agent, $referrer_url, $device_port, 1);
return $result ? $this->success() : $this->error();
}
}
\ No newline at end of file
... ...
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
... ...
<?php
namespace App\Http\Controllers\Cside;
use App\Http\Controllers\Controller;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\JsonResponse;
class BaseController extends Controller
{
const SUCCESS = 200;
const ERROR = 400;
protected $param = [];//所有请求参数
protected $token = ''; //token
protected $request = [];//助手函数
protected $page = 1;//当前页
protected $row = 20;//每页条数
protected $header = [];//设置请求头参数
protected $order = 'id';
protected $map = [];//处理后的参数
protected $uid = 0;
protected $user = [];//当前登录用户详情
public function __construct(){
}
/**
* 响应
* @throws HttpResponseException
*/
public function response($msg = null,string $code = self::SUCCESS,$data = [],$result_code = 200,$type = 'application/json'): JsonResponse
{
$result = [
'msg' => $msg,
'code' => $code,
'data' => $data,
];
$this->header['Content-Type'] = $type;
$this->header['token'] = $this->token;
$response = response($result,$result_code,$this->header);
throw new HttpResponseException($response);
}
}
... ...
<?php
namespace App\Http\Controllers\Cside\Visit;
use App\Enums\Common\Code;
use App\Http\Controllers\Cside\BaseController;
use App\Models\Project\Project;
use App\Models\SyncSubmitTask\SyncSubmitTask;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class VisitController extends BaseController
{
/**
* 客户访问埋点接口
*/
public function customerVisit(Request $request): \Illuminate\Http\JsonResponse
{
$data = $request->all();
if ($this->filter($request)){
$data = $this->visitInfoHandle($data);
//异步处理
if(!SyncSubmitTask::addTask(SyncSubmitTask::TYPE_VISIT, $data)){
$this->responseA([], 400, 'error');
}
}
//埋点成功
return response()->json([
'code' => Code::SUCCESS_NUM,
'msg' => '客户访问',
]);
}
public function filter($request){
if($request->getClientIp() == "127.0.0.1"){
return false;
}
//判断是否是爬虫
$isReptile = $this->isReptile($request);
if($isReptile){
return false;
}
//是否允许测试环境
$projectDomain = Project::getProjectByDomain($request->getHost());
$project = Project::find($projectDomain['project_id']??0);
if(empty($project)){
return false;
}
// 测试环境返回信息
if (FALSE !== strpos($request->getHost(), 'globalso.site') && !$project->is_record_test_visit) {
return false;
}
return true;
}
/**
* 埋点信息处理
*/
public function visitInfoHandle($data)
{
//referrer
if(preg_match('/google|facebook|bing|yahoo|youtobe|linkedin|messefrankfurt|yandex|tiktok|twitter|instagram|reddit|telegram|pinterest|tumblr/', $data['referrer_url'])){
}else if($data['referrer_url'] == null){
//直访用户
$data['referrer_url'] = "";
}else{
$data['referrer_url'] = "https://www.google.com/";
}
return $data;
}
/**
* 是否是爬虫访问
*/
public function isReptile($request): bool
{
$agent = $request->header('User-Agent');
if (!empty($agent)) {
$spiderSite= array(
"TencentTraveler",
"Baiduspider+",
"BaiduGame",
"Googlebot",
"msnbot",
"Sosospider+",
"Sogou web spider",
"ia_archiver",
"Yahoo! Slurp",
"YoudaoBot",
"Yahoo Slurp",
"MSNBot",
"Java (Often spam bot)",
"BaiDuSpider",
"Voila",
"Yandex bot",
"BSpider",
"twiceler",
"Sogou Spider",
"Speedy Spider",
"Google AdSense",
"Heritrix",
"Python-urllib",
"Alexa (IA Archiver)",
"Ask",
"Exabot",
"Custo",
"OutfoxBot/YodaoBot",
"yacy",
"SurveyBot",
"legs",
"lwp-trivial",
"Nutch",
"StackRambler",
"The web archive (IA Archiver)",
"Perl tool",
"MJ12bot",
"Netcraft",
"MSIECrawler",
"WGet tools",
"larbin",
"Fish search",
"yandex.com/bots",
"google.com/bot",
"bingbot",
"YandexMobileBot",
"BingPreview",
"AhrefsBot",
"bot"
);
$flag = 0;
foreach($spiderSite as $val) {
$str = strtolower($val);
if (strpos($agent, $str) !== false) {
$flag = 1;
}
}
if($flag == 1){
return true;
}else{
return false;
}
} else {
return false;
}
}
}
... ...
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
//'throttle:1000,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\CorsMiddleware::class,
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, class-string|string>
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'cors' => \App\Http\Middleware\CorsMiddleware::class,
];
}
... ...
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}
... ...
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class CorsMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$response = $next($request);
$response->header('Access-Control-Allow-Origin', "*");
$response->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
$response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Authorization');
if (strtolower($_SERVER['REQUEST_METHOD']) == 'options') {
exit;
}
return $response;
}
}
... ...
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [
//
];
}
... ...
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
//
];
}
... ...
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @param string|null ...$guards
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}
... ...
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}
... ...
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}
... ...
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}
... ...
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [
"api/customerVisit",
"api/inquiry",
"api/inquiryOtherInfo",
"api/website_html_handle",
];
}
... ...
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}
... ...
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
}
}
... ...
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}
... ...
<?php
namespace App\Providers;
use App\Events\UpdateHtml;
use App\Listeners\UpdateHtmlListener;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
//
}
}
... ...
<?php
namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to the "home" route for your application.
*
* This is used by Laravel authentication to redirect users after login.
*
* @var string
*/
public const HOME = '/home';
/**
* The controller namespace for the application.
*
* When present, controller route declarations will automatically be prefixed with this namespace.
*
* @var string|null
*/
// protected $namespace = 'App\\Http\\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
$sld_prefix = isset($_SERVER['HTTP_HOST']) ? explode('.',$_SERVER['HTTP_HOST'])[0] : "";
if($sld_prefix == 'm'){
$this->mapAmpRoutes();
}else{
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
});
}
}
/**
* Configure the rate limiters for the application.
*
* @return void
*/
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
});
}
/**
* @author Akun
* @date 2024/01/26 11:03
*/
protected function mapAmpRoutes(){
Route::namespace('App\Http\Controllers\Amp')
->group(base_path('routes/amp.php'));
}
}
... ...
<?php
/**
* phpQuery is a server-side, chainable, CSS3 selector driven
* Document Object Model (DOM) API based on jQuery JavaScript Library.
*
* @version 0.9.5
* @link http://code.google.com/p/phpquery/
* @link http://phpquery-library.blogspot.com/
* @link http://jquery.com/
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @package phpQuery
*/
// class names for instanceof
// TODO move them as class constants into phpQuery
define('DOMDOCUMENT', 'DOMDocument');
define('DOMELEMENT', 'DOMElement');
define('DOMNODELIST', 'DOMNodeList');
define('DOMNODE', 'DOMNode');
require_once(dirname(__FILE__).'/phpQuery/DOMEvent.php');
require_once(dirname(__FILE__).'/phpQuery/DOMDocumentWrapper.php');
require_once(dirname(__FILE__).'/phpQuery/phpQueryEvents.php');
require_once(dirname(__FILE__).'/phpQuery/Callback.php');
require_once(dirname(__FILE__).'/phpQuery/phpQueryObject.php');
require_once(dirname(__FILE__).'/phpQuery/compat/mbstring.php');
/**
* Static namespace for phpQuery functions.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
*/
abstract class phpQuery {
/**
* XXX: Workaround for mbstring problems
*
* @var bool
*/
public static $mbstringSupport = true;
public static $debug = false;
public static $documents = array();
public static $defaultDocumentID = null;
// public static $defaultDoctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"';
/**
* Applies only to HTML.
*
* @var unknown_type
*/
public static $defaultDoctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">';
public static $defaultCharset = 'UTF-8';
/**
* Static namespace for plugins.
*
* @var object
*/
public static $plugins = array();
/**
* List of loaded plugins.
*
* @var unknown_type
*/
public static $pluginsLoaded = array();
public static $pluginsMethods = array();
public static $pluginsStaticMethods = array();
public static $extendMethods = array();
/**
* @TODO implement
*/
public static $extendStaticMethods = array();
/**
* Hosts allowed for AJAX connections.
* Dot '.' means $_SERVER['HTTP_HOST'] (if any).
*
* @var array
*/
public static $ajaxAllowedHosts = array(
'.'
);
/**
* AJAX settings.
*
* @var array
* XXX should it be static or not ?
*/
public static $ajaxSettings = array(
'url' => '',//TODO
'global' => true,
'type' => "GET",
'timeout' => null,
'contentType' => "application/x-www-form-urlencoded",
'processData' => true,
// 'async' => true,
'data' => null,
'username' => null,
'password' => null,
'accepts' => array(
'xml' => "application/xml, text/xml",
'html' => "text/html",
'script' => "text/javascript, application/javascript",
'json' => "application/json, text/javascript",
'text' => "text/plain",
'_default' => "*/*"
)
);
public static $lastModified = null;
public static $active = 0;
public static $dumpCount = 0;
/**
* Multi-purpose function.
* Use pq() as shortcut.
*
* In below examples, $pq is any result of pq(); function.
*
* 1. Import markup into existing document (without any attaching):
* - Import into selected document:
* pq('<div/>') // DOESNT accept text nodes at beginning of input string !
* - Import into document with ID from $pq->getDocumentID():
* pq('<div/>', $pq->getDocumentID())
* - Import into same document as DOMNode belongs to:
* pq('<div/>', DOMNode)
* - Import into document from phpQuery object:
* pq('<div/>', $pq)
*
* 2. Run query:
* - Run query on last selected document:
* pq('div.myClass')
* - Run query on document with ID from $pq->getDocumentID():
* pq('div.myClass', $pq->getDocumentID())
* - Run query on same document as DOMNode belongs to and use node(s)as root for query:
* pq('div.myClass', DOMNode)
* - Run query on document from phpQuery object
* and use object's stack as root node(s) for query:
* pq('div.myClass', $pq)
*
* @param string|DOMNode|DOMNodeList|array $arg1 HTML markup, CSS Selector, DOMNode or array of DOMNodes
* @param string|phpQueryObject|DOMNode $context DOM ID from $pq->getDocumentID(), phpQuery object (determines also query root) or DOMNode (determines also query root)
*
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery|QueryTemplatesPhpQuery|false
* phpQuery object or false in case of error.
*/
public static function pq($arg1, $context = null) {
if ($arg1 instanceof DOMNODE && ! isset($context)) {
foreach(phpQuery::$documents as $documentWrapper) {
$compare = $arg1 instanceof DOMDocument
? $arg1 : $arg1->ownerDocument;
if ($documentWrapper->document->isSameNode($compare))
$context = $documentWrapper->id;
}
}
if (! $context) {
$domId = self::$defaultDocumentID;
if (! $domId)
throw new Exception("Can't use last created DOM, because there isn't any. Use phpQuery::newDocument() first.");
// } else if (is_object($context) && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
} else if (is_object($context) && $context instanceof phpQueryObject)
$domId = $context->getDocumentID();
else if ($context instanceof DOMDOCUMENT) {
$domId = self::getDocumentID($context);
if (! $domId) {
//throw new Exception('Orphaned DOMDocument');
$domId = self::newDocument($context)->getDocumentID();
}
} else if ($context instanceof DOMNODE) {
$domId = self::getDocumentID($context);
if (! $domId) {
throw new Exception('Orphaned DOMNode');
// $domId = self::newDocument($context->ownerDocument);
}
} else
$domId = $context;
if ($arg1 instanceof phpQueryObject) {
// if (is_object($arg1) && (get_class($arg1) == 'phpQueryObject' || $arg1 instanceof PHPQUERY || is_subclass_of($arg1, 'phpQueryObject'))) {
/**
* Return $arg1 or import $arg1 stack if document differs:
* pq(pq('<div/>'))
*/
if ($arg1->getDocumentID() == $domId)
return $arg1;
$class = get_class($arg1);
// support inheritance by passing old object to overloaded constructor
$phpQuery = $class != 'phpQuery'
? new $class($arg1, $domId)
: new phpQueryObject($domId);
$phpQuery->elements = array();
foreach($arg1->elements as $node)
$phpQuery->elements[] = $phpQuery->document->importNode($node, true);
return $phpQuery;
} else if ($arg1 instanceof DOMNODE || (is_array($arg1) && isset($arg1[0]) && $arg1[0] instanceof DOMNODE)) {
/*
* Wrap DOM nodes with phpQuery object, import into document when needed:
* pq(array($domNode1, $domNode2))
*/
$phpQuery = new phpQueryObject($domId);
if (!($arg1 instanceof DOMNODELIST) && ! is_array($arg1))
$arg1 = array($arg1);
$phpQuery->elements = array();
foreach($arg1 as $node) {
$sameDocument = $node->ownerDocument instanceof DOMDOCUMENT
&& ! $node->ownerDocument->isSameNode($phpQuery->document);
$phpQuery->elements[] = $sameDocument
? $phpQuery->document->importNode($node, true)
: $node;
}
return $phpQuery;
} else if (self::isMarkup($arg1)) {
/**
* Import HTML:
* pq('<div/>')
*/
$phpQuery = new phpQueryObject($domId);
return $phpQuery->newInstance(
$phpQuery->documentWrapper->import($arg1)
);
} else {
/**
* Run CSS query:
* pq('div.myClass')
*/
$phpQuery = new phpQueryObject($domId);
// if ($context && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
if ($context && $context instanceof phpQueryObject)
$phpQuery->elements = $context->elements;
else if ($context && $context instanceof DOMNODELIST) {
$phpQuery->elements = array();
foreach($context as $node)
$phpQuery->elements[] = $node;
} else if ($context && $context instanceof DOMNODE)
$phpQuery->elements = array($context);
return $phpQuery->find($arg1);
}
}
/**
* Sets default document to $id. Document has to be loaded prior
* to using this method.
* $id can be retrived via getDocumentID() or getDocumentIDRef().
*
* @param unknown_type $id
*/
public static function selectDocument($id) {
$id = self::getDocumentID($id);
self::debug("Selecting document '$id' as default one");
self::$defaultDocumentID = self::getDocumentID($id);
}
/**
* Returns document with id $id or last used as phpQueryObject.
* $id can be retrived via getDocumentID() or getDocumentIDRef().
* Chainable.
*
* @see phpQuery::selectDocument()
* @param unknown_type $id
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function getDocument($id = null) {
if ($id)
phpQuery::selectDocument($id);
else
$id = phpQuery::$defaultDocumentID;
return new phpQueryObject($id);
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocument($markup = null, $contentType = null) {
if (! $markup)
$markup = '';
$documentID = phpQuery::createDocumentWrapper($markup, $contentType);
return new phpQueryObject($documentID);
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentHTML($markup = null, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocument($markup, "text/html{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentXML($markup = null, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocument($markup, "text/xml{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentXHTML($markup = null, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocument($markup, "application/xhtml+xml{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentPHP($markup = null, $contentType = "text/html") {
// TODO pass charset to phpToMarkup if possible (use DOMDocumentWrapper function)
$markup = phpQuery::phpToMarkup($markup, self::$defaultCharset);
return self::newDocument($markup, $contentType);
}
public static function phpToMarkup($php, $charset = 'utf-8') {
$regexes = array(
'@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)<'.'?php?(.*?)(?:\\?>)([^\']*)\'@s',
'@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)<'.'?php?(.*?)(?:\\?>)([^"]*)"@s',
);
foreach($regexes as $regex)
while (preg_match($regex, $php, $matches)) {
$php = preg_replace_callback(
$regex,
// create_function('$m, $charset = "'.$charset.'"',
// 'return $m[1].$m[2]
// .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
// .$m[5].$m[2];'
// ),
array('phpQuery', '_phpToMarkupCallback'),
$php
);
}
$regex = '@(^|>[^<]*)+?(<\?php(.*?)(\?>))@s';
//preg_match_all($regex, $php, $matches);
//var_dump($matches);
$php = preg_replace($regex, '\\1<php><!-- \\3 --></php>', $php);
return $php;
}
public static function _phpToMarkupCallback($php, $charset = 'utf-8') {
return $m[1].$m[2]
.htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
.$m[5].$m[2];
}
public static function _markupToPHPCallback($m) {
return "<"."?php ".htmlspecialchars_decode($m[1])." ?".">";
}
/**
* Converts document markup containing PHP code generated by phpQuery::php()
* into valid (executable) PHP code syntax.
*
* @param string|phpQueryObject $content
* @return string PHP code.
*/
public static function markupToPHP($content) {
if ($content instanceof phpQueryObject)
$content = $content->markupOuter();
/* <php>...</php> to <?php...? > */
$content = preg_replace_callback(
'@<php>\s*<!--(.*?)-->\s*</php>@s',
// create_function('$m',
// 'return "<'.'?php ".htmlspecialchars_decode($m[1])." ?'.'>";'
// ),
array('phpQuery', '_markupToPHPCallback'),
$content
);
/* <node attr='< ?php ? >'> extra space added to save highlighters */
$regexes = array(
'@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^\']*)\'@s',
'@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^"]*)"@s',
);
foreach($regexes as $regex)
while (preg_match($regex, $content))
$content = preg_replace_callback(
$regex,
create_function('$m',
'return $m[1].$m[2].$m[3]."<?php "
.str_replace(
array("%20", "%3E", "%09", "&#10;", "&#9;", "%7B", "%24", "%7D", "%22", "%5B", "%5D"),
array(" ", ">", " ", "\n", " ", "{", "$", "}", \'"\', "[", "]"),
htmlspecialchars_decode($m[4])
)
." ?>".$m[5].$m[2];'
),
$content
);
return $content;
}
/**
* Creates new document from file $file.
* Chainable.
*
* @param string $file URLs allowed. See File wrapper page at php.net for more supported sources.
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFile($file, $contentType = null) {
$documentID = self::createDocumentWrapper(
file_get_contents($file), $contentType
);
return new phpQueryObject($documentID);
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFileHTML($file, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocumentFile($file, "text/html{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFileXML($file, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocumentFile($file, "text/xml{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFileXHTML($file, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocumentFile($file, "application/xhtml+xml{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFilePHP($file, $contentType = null) {
return self::newDocumentPHP(file_get_contents($file), $contentType);
}
/**
* Reuses existing DOMDocument object.
* Chainable.
*
* @param $document DOMDocument
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
* @TODO support DOMDocument
*/
public static function loadDocument($document) {
// TODO
die('TODO loadDocument');
}
/**
* Enter description here...
*
* @param unknown_type $html
* @param unknown_type $domId
* @return unknown New DOM ID
* @todo support PHP tags in input
* @todo support passing DOMDocument object from self::loadDocument
*/
protected static function createDocumentWrapper($html, $contentType = null, $documentID = null) {
if (function_exists('domxml_open_mem'))
throw new Exception("Old PHP4 DOM XML extension detected. phpQuery won't work until this extension is enabled.");
// $id = $documentID
// ? $documentID
// : md5(microtime());
$document = null;
if ($html instanceof DOMDOCUMENT) {
if (self::getDocumentID($html)) {
// document already exists in phpQuery::$documents, make a copy
$document = clone $html;
} else {
// new document, add it to phpQuery::$documents
$wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
}
} else {
$wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
}
// $wrapper->id = $id;
// bind document
phpQuery::$documents[$wrapper->id] = $wrapper;
// remember last loaded document
phpQuery::selectDocument($wrapper->id);
return $wrapper->id;
}
/**
* Extend class namespace.
*
* @param string|array $target
* @param array $source
* @TODO support string $source
* @return unknown_type
*/
public static function extend($target, $source) {
switch($target) {
case 'phpQueryObject':
$targetRef = &self::$extendMethods;
$targetRef2 = &self::$pluginsMethods;
break;
case 'phpQuery':
$targetRef = &self::$extendStaticMethods;
$targetRef2 = &self::$pluginsStaticMethods;
break;
default:
throw new Exception("Unsupported \$target type");
}
if (is_string($source))
$source = array($source => $source);
foreach($source as $method => $callback) {
if (isset($targetRef[$method])) {
// throw new Exception
self::debug("Duplicate method '{$method}', can\'t extend '{$target}'");
continue;
}
if (isset($targetRef2[$method])) {
// throw new Exception
self::debug("Duplicate method '{$method}' from plugin '{$targetRef2[$method]}',"
." can\'t extend '{$target}'");
continue;
}
$targetRef[$method] = $callback;
}
return true;
}
/**
* Extend phpQuery with $class from $file.
*
* @param string $class Extending class name. Real class name can be prepended phpQuery_.
* @param string $file Filename to include. Defaults to "{$class}.php".
*/
public static function plugin($class, $file = null) {
// TODO $class checked agains phpQuery_$class
// if (strpos($class, 'phpQuery') === 0)
// $class = substr($class, 8);
if (in_array($class, self::$pluginsLoaded))
return true;
if (! $file)
$file = $class.'.php';
$objectClassExists = class_exists('phpQueryObjectPlugin_'.$class);
$staticClassExists = class_exists('phpQueryPlugin_'.$class);
if (! $objectClassExists && ! $staticClassExists)
require_once($file);
self::$pluginsLoaded[] = $class;
// static methods
if (class_exists('phpQueryPlugin_'.$class)) {
$realClass = 'phpQueryPlugin_'.$class;
$vars = get_class_vars($realClass);
$loop = isset($vars['phpQueryMethods'])
&& ! is_null($vars['phpQueryMethods'])
? $vars['phpQueryMethods']
: get_class_methods($realClass);
foreach($loop as $method) {
if ($method == '__initialize')
continue;
if (! is_callable(array($realClass, $method)))
continue;
if (isset(self::$pluginsStaticMethods[$method])) {
throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsStaticMethods[$method]."'");
return;
}
self::$pluginsStaticMethods[$method] = $class;
}
if (method_exists($realClass, '__initialize'))
call_user_func_array(array($realClass, '__initialize'), array());
}
// object methods
if (class_exists('phpQueryObjectPlugin_'.$class)) {
$realClass = 'phpQueryObjectPlugin_'.$class;
$vars = get_class_vars($realClass);
$loop = isset($vars['phpQueryMethods'])
&& ! is_null($vars['phpQueryMethods'])
? $vars['phpQueryMethods']
: get_class_methods($realClass);
foreach($loop as $method) {
if (! is_callable(array($realClass, $method)))
continue;
if (isset(self::$pluginsMethods[$method])) {
throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsMethods[$method]."'");
continue;
}
self::$pluginsMethods[$method] = $class;
}
}
return true;
}
/**
* Unloades all or specified document from memory.
*
* @param mixed $documentID @see phpQuery::getDocumentID() for supported types.
*/
public static function unloadDocuments($id = null) {
if (isset($id)) {
if ($id = self::getDocumentID($id))
unset(phpQuery::$documents[$id]);
} else {
foreach(phpQuery::$documents as $k => $v) {
unset(phpQuery::$documents[$k]);
}
}
}
/**
* Parses phpQuery object or HTML result against PHP tags and makes them active.
*
* @param phpQuery|string $content
* @deprecated
* @return string
*/
public static function unsafePHPTags($content) {
return self::markupToPHP($content);
}
public static function DOMNodeListToArray($DOMNodeList) {
$array = array();
if (! $DOMNodeList)
return $array;
foreach($DOMNodeList as $node)
$array[] = $node;
return $array;
}
/**
* Checks if $input is HTML string, which has to start with '<'.
*
* @deprecated
* @param String $input
* @return Bool
* @todo still used ?
*/
public static function isMarkup($input) {
return ! is_array($input) && substr(trim($input), 0, 1) == '<';
}
public static function debug($text) {
if (self::$debug)
print var_dump($text);
}
/**
* Make an AJAX request.
*
* @param array See $options http://docs.jquery.com/Ajax/jQuery.ajax#toptions
* Additional options are:
* 'document' - document for global events, @see phpQuery::getDocumentID()
* 'referer' - implemented
* 'requested_with' - TODO; not implemented (X-Requested-With)
* @return Zend_Http_Client
* @link http://docs.jquery.com/Ajax/jQuery.ajax
*
* @TODO $options['cache']
* @TODO $options['processData']
* @TODO $options['xhr']
* @TODO $options['data'] as string
* @TODO XHR interface
*/
public static function ajax($options = array(), $xhr = null) {
$options = array_merge(
self::$ajaxSettings, $options
);
$documentID = isset($options['document'])
? self::getDocumentID($options['document'])
: null;
if ($xhr) {
// reuse existing XHR object, but clean it up
$client = $xhr;
// $client->setParameterPost(null);
// $client->setParameterGet(null);
$client->setAuth(false);
$client->setHeaders("If-Modified-Since", null);
$client->setHeaders("Referer", null);
$client->resetParameters();
} else {
// create new XHR object
require_once('Zend/Http/Client.php');
$client = new Zend_Http_Client();
$client->setCookieJar();
}
if (isset($options['timeout']))
$client->setConfig(array(
'timeout' => $options['timeout'],
));
// 'maxredirects' => 0,
foreach(self::$ajaxAllowedHosts as $k => $host)
if ($host == '.' && isset($_SERVER['HTTP_HOST']))
self::$ajaxAllowedHosts[$k] = $_SERVER['HTTP_HOST'];
$host = parse_url($options['url'], PHP_URL_HOST);
if (! in_array($host, self::$ajaxAllowedHosts)) {
throw new Exception("Request not permitted, host '$host' not present in "
."phpQuery::\$ajaxAllowedHosts");
}
// JSONP
$jsre = "/=\\?(&|$)/";
if (isset($options['dataType']) && $options['dataType'] == 'jsonp') {
$jsonpCallbackParam = $options['jsonp']
? $options['jsonp'] : 'callback';
if (strtolower($options['type']) == 'get') {
if (! preg_match($jsre, $options['url'])) {
$sep = strpos($options['url'], '?')
? '&' : '?';
$options['url'] .= "$sep$jsonpCallbackParam=?";
}
} else if ($options['data']) {
$jsonp = false;
foreach($options['data'] as $n => $v) {
if ($v == '?')
$jsonp = true;
}
if (! $jsonp) {
$options['data'][$jsonpCallbackParam] = '?';
}
}
$options['dataType'] = 'json';
}
if (isset($options['dataType']) && $options['dataType'] == 'json') {
$jsonpCallback = 'json_'.md5(microtime());
$jsonpData = $jsonpUrl = false;
if ($options['data']) {
foreach($options['data'] as $n => $v) {
if ($v == '?')
$jsonpData = $n;
}
}
if (preg_match($jsre, $options['url']))
$jsonpUrl = true;
if ($jsonpData !== false || $jsonpUrl) {
// remember callback name for httpData()
$options['_jsonp'] = $jsonpCallback;
if ($jsonpData !== false)
$options['data'][$jsonpData] = $jsonpCallback;
if ($jsonpUrl)
$options['url'] = preg_replace($jsre, "=$jsonpCallback\\1", $options['url']);
}
}
$client->setUri($options['url']);
$client->setMethod(strtoupper($options['type']));
if (isset($options['referer']) && $options['referer'])
$client->setHeaders('Referer', $options['referer']);
$client->setHeaders(array(
// 'content-type' => $options['contentType'],
'User-Agent' => 'Mozilla/5.0 (X11; U; Linux x86; en-US; rv:1.9.0.5) Gecko'
.'/2008122010 Firefox/3.0.5',
// TODO custom charset
'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
// 'Connection' => 'keep-alive',
// 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language' => 'en-us,en;q=0.5',
));
if ($options['username'])
$client->setAuth($options['username'], $options['password']);
if (isset($options['ifModified']) && $options['ifModified'])
$client->setHeaders("If-Modified-Since",
self::$lastModified
? self::$lastModified
: "Thu, 01 Jan 1970 00:00:00 GMT"
);
$client->setHeaders("Accept",
isset($options['dataType'])
&& isset(self::$ajaxSettings['accepts'][ $options['dataType'] ])
? self::$ajaxSettings['accepts'][ $options['dataType'] ].", */*"
: self::$ajaxSettings['accepts']['_default']
);
// TODO $options['processData']
if ($options['data'] instanceof phpQueryObject) {
$serialized = $options['data']->serializeArray($options['data']);
$options['data'] = array();
foreach($serialized as $r)
$options['data'][ $r['name'] ] = $r['value'];
}
if (strtolower($options['type']) == 'get') {
$client->setParameterGet($options['data']);
} else if (strtolower($options['type']) == 'post') {
$client->setEncType($options['contentType']);
$client->setParameterPost($options['data']);
}
if (self::$active == 0 && $options['global'])
phpQueryEvents::trigger($documentID, 'ajaxStart');
self::$active++;
// beforeSend callback
if (isset($options['beforeSend']) && $options['beforeSend'])
phpQuery::callbackRun($options['beforeSend'], array($client));
// ajaxSend event
if ($options['global'])
phpQueryEvents::trigger($documentID, 'ajaxSend', array($client, $options));
if (phpQuery::$debug) {
self::debug("{$options['type']}: {$options['url']}\n");
self::debug("Options: <pre>".var_export($options, true)."</pre>\n");
// if ($client->getCookieJar())
// self::debug("Cookies: <pre>".var_export($client->getCookieJar()->getMatchingCookies($options['url']), true)."</pre>\n");
}
// request
$response = $client->request();
if (phpQuery::$debug) {
self::debug('Status: '.$response->getStatus().' / '.$response->getMessage());
self::debug($client->getLastRequest());
self::debug($response->getHeaders());
}
if ($response->isSuccessful()) {
// XXX tempolary
self::$lastModified = $response->getHeader('Last-Modified');
$data = self::httpData($response->getBody(), $options['dataType'], $options);
if (isset($options['success']) && $options['success'])
phpQuery::callbackRun($options['success'], array($data, $response->getStatus(), $options));
if ($options['global'])
phpQueryEvents::trigger($documentID, 'ajaxSuccess', array($client, $options));
} else {
if (isset($options['error']) && $options['error'])
phpQuery::callbackRun($options['error'], array($client, $response->getStatus(), $response->getMessage()));
if ($options['global'])
phpQueryEvents::trigger($documentID, 'ajaxError', array($client, /*$response->getStatus(),*/$response->getMessage(), $options));
}
if (isset($options['complete']) && $options['complete'])
phpQuery::callbackRun($options['complete'], array($client, $response->getStatus()));
if ($options['global'])
phpQueryEvents::trigger($documentID, 'ajaxComplete', array($client, $options));
if ($options['global'] && ! --self::$active)
phpQueryEvents::trigger($documentID, 'ajaxStop');
return $client;
// if (is_null($domId))
// $domId = self::$defaultDocumentID ? self::$defaultDocumentID : false;
// return new phpQueryAjaxResponse($response, $domId);
}
protected static function httpData($data, $type, $options) {
if (isset($options['dataFilter']) && $options['dataFilter'])
$data = self::callbackRun($options['dataFilter'], array($data, $type));
if (is_string($data)) {
if ($type == "json") {
if (isset($options['_jsonp']) && $options['_jsonp']) {
$data = preg_replace('/^\s*\w+\((.*)\)\s*$/s', '$1', $data);
}
$data = self::parseJSON($data);
}
}
return $data;
}
/**
* Enter description here...
*
* @param array|phpQuery $data
*
*/
public static function param($data) {
return http_build_query($data, null, '&');
}
public static function get($url, $data = null, $callback = null, $type = null) {
if (!is_array($data)) {
$callback = $data;
$data = null;
}
// TODO some array_values on this shit
return phpQuery::ajax(array(
'type' => 'GET',
'url' => $url,
'data' => $data,
'success' => $callback,
'dataType' => $type,
));
}
public static function post($url, $data = null, $callback = null, $type = null) {
if (!is_array($data)) {
$callback = $data;
$data = null;
}
return phpQuery::ajax(array(
'type' => 'POST',
'url' => $url,
'data' => $data,
'success' => $callback,
'dataType' => $type,
));
}
public static function getJSON($url, $data = null, $callback = null) {
if (!is_array($data)) {
$callback = $data;
$data = null;
}
// TODO some array_values on this shit
return phpQuery::ajax(array(
'type' => 'GET',
'url' => $url,
'data' => $data,
'success' => $callback,
'dataType' => 'json',
));
}
public static function ajaxSetup($options) {
self::$ajaxSettings = array_merge(
self::$ajaxSettings,
$options
);
}
public static function ajaxAllowHost($host1, $host2 = null, $host3 = null) {
$loop = is_array($host1)
? $host1
: func_get_args();
foreach($loop as $host) {
if ($host && ! in_array($host, phpQuery::$ajaxAllowedHosts)) {
phpQuery::$ajaxAllowedHosts[] = $host;
}
}
}
public static function ajaxAllowURL($url1, $url2 = null, $url3 = null) {
$loop = is_array($url1)
? $url1
: func_get_args();
foreach($loop as $url)
phpQuery::ajaxAllowHost(parse_url($url, PHP_URL_HOST));
}
/**
* Returns JSON representation of $data.
*
* @static
* @param mixed $data
* @return string
*/
public static function toJSON($data) {
if (function_exists('json_encode'))
return json_encode($data);
require_once('Zend/Json/Encoder.php');
return Zend_Json_Encoder::encode($data);
}
/**
* Parses JSON into proper PHP type.
*
* @static
* @param string $json
* @return mixed
*/
public static function parseJSON($json) {
if (function_exists('json_decode')) {
$return = json_decode(trim($json), true);
// json_decode and UTF8 issues
if (isset($return))
return $return;
}
require_once('Zend/Json/Decoder.php');
return Zend_Json_Decoder::decode($json);
}
/**
* Returns source's document ID.
*
* @param $source DOMNode|phpQueryObject
* @return string
*/
public static function getDocumentID($source) {
if ($source instanceof DOMDOCUMENT) {
foreach(phpQuery::$documents as $id => $document) {
if ($source->isSameNode($document->document))
return $id;
}
} else if ($source instanceof DOMNODE) {
foreach(phpQuery::$documents as $id => $document) {
if ($source->ownerDocument->isSameNode($document->document))
return $id;
}
} else if ($source instanceof phpQueryObject)
return $source->getDocumentID();
else if (is_string($source) && isset(phpQuery::$documents[$source]))
return $source;
}
/**
* Get DOMDocument object related to $source.
* Returns null if such document doesn't exist.
*
* @param $source DOMNode|phpQueryObject|string
* @return string
*/
public static function getDOMDocument($source) {
if ($source instanceof DOMDOCUMENT)
return $source;
$source = self::getDocumentID($source);
return $source
? self::$documents[$id]['document']
: null;
}
// UTILITIES
// http://docs.jquery.com/Utilities
/**
*
* @return unknown_type
* @link http://docs.jquery.com/Utilities/jQuery.makeArray
*/
public static function makeArray($obj) {
$array = array();
if (is_object($object) && $object instanceof DOMNODELIST) {
foreach($object as $value)
$array[] = $value;
} else if (is_object($object) && ! ($object instanceof Iterator)) {
foreach(get_object_vars($object) as $name => $value)
$array[0][$name] = $value;
} else {
foreach($object as $name => $value)
$array[0][$name] = $value;
}
return $array;
}
public static function inArray($value, $array) {
return in_array($value, $array);
}
/**
*
* @param $object
* @param $callback
* @return unknown_type
* @link http://docs.jquery.com/Utilities/jQuery.each
*/
public static function each($object, $callback, $param1 = null, $param2 = null, $param3 = null) {
$paramStructure = null;
if (func_num_args() > 2) {
$paramStructure = func_get_args();
$paramStructure = array_slice($paramStructure, 2);
}
if (is_object($object) && ! ($object instanceof Iterator)) {
foreach(get_object_vars($object) as $name => $value)
phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
} else {
foreach($object as $name => $value)
phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
}
}
/**
*
* @link http://docs.jquery.com/Utilities/jQuery.map
*/
public static function map($array, $callback, $param1 = null, $param2 = null, $param3 = null) {
$result = array();
$paramStructure = null;
if (func_num_args() > 2) {
$paramStructure = func_get_args();
$paramStructure = array_slice($paramStructure, 2);
}
foreach($array as $v) {
$vv = phpQuery::callbackRun($callback, array($v), $paramStructure);
// $callbackArgs = $args;
// foreach($args as $i => $arg) {
// $callbackArgs[$i] = $arg instanceof CallbackParam
// ? $v
// : $arg;
// }
// $vv = call_user_func_array($callback, $callbackArgs);
if (is_array($vv)) {
foreach($vv as $vvv)
$result[] = $vvv;
} else if ($vv !== null) {
$result[] = $vv;
}
}
return $result;
}
/**
*
* @param $callback Callback
* @param $params
* @param $paramStructure
* @return unknown_type
*/
public static function callbackRun($callback, $params = array(), $paramStructure = null) {
if (! $callback)
return;
if ($callback instanceof CallbackParameterToReference) {
// TODO support ParamStructure to select which $param push to reference
if (isset($params[0]))
$callback->callback = $params[0];
return true;
}
if ($callback instanceof Callback) {
$paramStructure = $callback->params;
$callback = $callback->callback;
}
if (! $paramStructure)
return call_user_func_array($callback, $params);
$p = 0;
foreach($paramStructure as $i => $v) {
$paramStructure[$i] = $v instanceof CallbackParam
? $params[$p++]
: $v;
}
return call_user_func_array($callback, $paramStructure);
}
/**
* Merge 2 phpQuery objects.
* @param array $one
* @param array $two
* @protected
* @todo node lists, phpQueryObject
*/
public static function merge($one, $two) {
$elements = $one->elements;
foreach($two->elements as $node) {
$exists = false;
foreach($elements as $node2) {
if ($node2->isSameNode($node))
$exists = true;
}
if (! $exists)
$elements[] = $node;
}
return $elements;
// $one = $one->newInstance();
// $one->elements = $elements;
// return $one;
}
/**
*
* @param $array
* @param $callback
* @param $invert
* @return unknown_type
* @link http://docs.jquery.com/Utilities/jQuery.grep
*/
public static function grep($array, $callback, $invert = false) {
$result = array();
foreach($array as $k => $v) {
$r = call_user_func_array($callback, array($v, $k));
if ($r === !(bool)$invert)
$result[] = $v;
}
return $result;
}
public static function unique($array) {
return array_unique($array);
}
/**
*
* @param $function
* @return unknown_type
* @TODO there are problems with non-static methods, second parameter pass it
* but doesnt verify is method is really callable
*/
public static function isFunction($function) {
return is_callable($function);
}
public static function trim($str) {
return trim($str);
}
/* PLUGINS NAMESPACE */
/**
*
* @param $url
* @param $callback
* @param $param1
* @param $param2
* @param $param3
* @return phpQueryObject
*/
public static function browserGet($url, $callback, $param1 = null, $param2 = null, $param3 = null) {
if (self::plugin('WebBrowser')) {
$params = func_get_args();
return self::callbackRun(array(self::$plugins, 'browserGet'), $params);
} else {
self::debug('WebBrowser plugin not available...');
}
}
/**
*
* @param $url
* @param $data
* @param $callback
* @param $param1
* @param $param2
* @param $param3
* @return phpQueryObject
*/
public static function browserPost($url, $data, $callback, $param1 = null, $param2 = null, $param3 = null) {
if (self::plugin('WebBrowser')) {
$params = func_get_args();
return self::callbackRun(array(self::$plugins, 'browserPost'), $params);
} else {
self::debug('WebBrowser plugin not available...');
}
}
/**
*
* @param $ajaxSettings
* @param $callback
* @param $param1
* @param $param2
* @param $param3
* @return phpQueryObject
*/
public static function browser($ajaxSettings, $callback, $param1 = null, $param2 = null, $param3 = null) {
if (self::plugin('WebBrowser')) {
$params = func_get_args();
return self::callbackRun(array(self::$plugins, 'browser'), $params);
} else {
self::debug('WebBrowser plugin not available...');
}
}
/**
*
* @param $code
* @return string
*/
public static function php($code) {
return self::code('php', $code);
}
/**
*
* @param $type
* @param $code
* @return string
*/
public static function code($type, $code) {
return "<$type><!-- ".trim($code)." --></$type>";
}
public static function __callStatic($method, $params) {
return call_user_func_array(
array(phpQuery::$plugins, $method),
$params
);
}
protected static function dataSetupNode($node, $documentID) {
// search are return if alredy exists
foreach(phpQuery::$documents[$documentID]->dataNodes as $dataNode) {
if ($node->isSameNode($dataNode))
return $dataNode;
}
// if doesn't, add it
phpQuery::$documents[$documentID]->dataNodes[] = $node;
return $node;
}
protected static function dataRemoveNode($node, $documentID) {
// search are return if alredy exists
foreach(phpQuery::$documents[$documentID]->dataNodes as $k => $dataNode) {
if ($node->isSameNode($dataNode)) {
unset(self::$documents[$documentID]->dataNodes[$k]);
unset(self::$documents[$documentID]->data[ $dataNode->dataID ]);
}
}
}
public static function data($node, $name, $data, $documentID = null) {
if (! $documentID)
// TODO check if this works
$documentID = self::getDocumentID($node);
$document = phpQuery::$documents[$documentID];
$node = self::dataSetupNode($node, $documentID);
if (! isset($node->dataID))
$node->dataID = ++phpQuery::$documents[$documentID]->uuid;
$id = $node->dataID;
if (! isset($document->data[$id]))
$document->data[$id] = array();
if (! is_null($data))
$document->data[$id][$name] = $data;
if ($name) {
if (isset($document->data[$id][$name]))
return $document->data[$id][$name];
} else
return $id;
}
public static function removeData($node, $name, $documentID) {
if (! $documentID)
// TODO check if this works
$documentID = self::getDocumentID($node);
$document = phpQuery::$documents[$documentID];
$node = self::dataSetupNode($node, $documentID);
$id = $node->dataID;
if ($name) {
if (isset($document->data[$id][$name]))
unset($document->data[$id][$name]);
$name = null;
foreach($document->data[$id] as $name)
break;
if (! $name)
self::removeData($node, $name, $documentID);
} else {
self::dataRemoveNode($node, $documentID);
}
}
}
/**
* Plugins static namespace class.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
* @todo move plugin methods here (as statics)
*/
class phpQueryPlugins {
public function __call($method, $args) {
if (isset(phpQuery::$extendStaticMethods[$method])) {
$return = call_user_func_array(
phpQuery::$extendStaticMethods[$method],
$args
);
} else if (isset(phpQuery::$pluginsStaticMethods[$method])) {
$class = phpQuery::$pluginsStaticMethods[$method];
$realClass = "phpQueryPlugin_$class";
$return = call_user_func_array(
array($realClass, $method),
$args
);
return isset($return)
? $return
: $this;
} else
throw new Exception("Method '{$method}' doesnt exist");
}
}
/**
* Shortcut to phpQuery::pq($arg1, $context)
* Chainable.
*
* @see phpQuery::pq()
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
*/
function pq($arg1, $context = null) {
$args = func_get_args();
return call_user_func_array(
array('phpQuery', 'pq'),
$args
);
}
// add plugins dir and Zend framework to include path
set_include_path(
get_include_path()
.PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/'
.PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/plugins/'
);
// why ? no __call nor __get for statics in php...
// XXX __callStatic will be available in PHP 5.3
phpQuery::$plugins = new phpQueryPlugins();
// include bootstrap file (personal library config)
if (file_exists(dirname(__FILE__).'/phpQuery/bootstrap.php'))
require_once dirname(__FILE__).'/phpQuery/bootstrap.php';
\ No newline at end of file
... ...
<?php
interface ICallbackNamed {
function hasName();
function getName();
}
/**
* Callback class introduces currying-like pattern.
*
* Example:
* function foo($param1, $param2, $param3) {
* var_dump($param1, $param2, $param3);
* }
* $fooCurried = new Callback('foo',
* 'param1 is now statically set',
* new CallbackParam, new CallbackParam
* );
* phpQuery::callbackRun($fooCurried,
* array('param2 value', 'param3 value'
* );
*
* Callback class is supported in all phpQuery methods which accepts callbacks.
*
* @link http://code.google.com/p/phpquery/wiki/Callbacks#Param_Structures
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*
* @TODO??? return fake forwarding function created via create_function
* @TODO honor paramStructure
*/
class Callback
implements ICallbackNamed {
public $callback = null;
public $params = null;
protected $name;
public function __construct($callback, $param1 = null, $param2 = null,
$param3 = null) {
$params = func_get_args();
$params = array_slice($params, 1);
if ($callback instanceof Callback) {
// TODO implement recurention
} else {
$this->callback = $callback;
$this->params = $params;
}
}
public function getName() {
return 'Callback: '.$this->name;
}
public function hasName() {
return isset($this->name) && $this->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
// TODO test me
// public function addParams() {
// $params = func_get_args();
// return new Callback($this->callback, $this->params+$params);
// }
}
/**
* Shorthand for new Callback(create_function(...), ...);
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
class CallbackBody extends Callback {
public function __construct($paramList, $code, $param1 = null, $param2 = null,
$param3 = null) {
$params = func_get_args();
$params = array_slice($params, 2);
$this->callback = create_function($paramList, $code);
$this->params = $params;
}
}
/**
* Callback type which on execution returns reference passed during creation.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
class CallbackReturnReference extends Callback
implements ICallbackNamed {
protected $reference;
public function __construct(&$reference, $name = null){
$this->reference =& $reference;
$this->callback = array($this, 'callback');
}
public function callback() {
return $this->reference;
}
public function getName() {
return 'Callback: '.$this->name;
}
public function hasName() {
return isset($this->name) && $this->name;
}
}
/**
* Callback type which on execution returns value passed during creation.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
class CallbackReturnValue extends Callback
implements ICallbackNamed {
protected $value;
protected $name;
public function __construct($value, $name = null){
$this->value =& $value;
$this->name = $name;
$this->callback = array($this, 'callback');
}
public function callback() {
return $this->value;
}
public function __toString() {
return $this->getName();
}
public function getName() {
return 'Callback: '.$this->name;
}
public function hasName() {
return isset($this->name) && $this->name;
}
}
/**
* CallbackParameterToReference can be used when we don't really want a callback,
* only parameter passed to it. CallbackParameterToReference takes first
* parameter's value and passes it to reference.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
class CallbackParameterToReference extends Callback {
/**
* @param $reference
* @TODO implement $paramIndex;
* param index choose which callback param will be passed to reference
*/
public function __construct(&$reference){
$this->callback =& $reference;
}
}
//class CallbackReference extends Callback {
// /**
// *
// * @param $reference
// * @param $paramIndex
// * @todo implement $paramIndex; param index choose which callback param will be passed to reference
// */
// public function __construct(&$reference, $name = null){
// $this->callback =& $reference;
// }
//}
class CallbackParam {}
\ No newline at end of file
... ...
<?php
/**
* DOMDocumentWrapper class simplifies work with DOMDocument.
*
* Know bug:
* - in XHTML fragments, <br /> changes to <br clear="none" />
*
* @todo check XML catalogs compatibility
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
*/
class DOMDocumentWrapper {
/**
* @var DOMDocument
*/
public $document;
public $id;
/**
* @todo Rewrite as method and quess if null.
* @var unknown_type
*/
public $contentType = '';
public $xpath;
public $uuid = 0;
public $data = array();
public $dataNodes = array();
public $events = array();
public $eventsNodes = array();
public $eventsGlobal = array();
/**
* @TODO iframes support http://code.google.com/p/phpquery/issues/detail?id=28
* @var unknown_type
*/
public $frames = array();
/**
* Document root, by default equals to document itself.
* Used by documentFragments.
*
* @var DOMNode
*/
public $root;
public $isDocumentFragment;
public $isXML = false;
public $isXHTML = false;
public $isHTML = false;
public $charset;
public function __construct($markup = null, $contentType = null, $newDocumentID = null) {
if (isset($markup))
$this->load($markup, $contentType, $newDocumentID);
$this->id = $newDocumentID
? $newDocumentID
: md5(microtime());
}
public function load($markup, $contentType = null, $newDocumentID = null) {
// phpQuery::$documents[$id] = $this;
$this->contentType = strtolower($contentType);
if ($markup instanceof DOMDOCUMENT) {
$this->document = $markup;
$this->root = $this->document;
$this->charset = $this->document->encoding;
// TODO isDocumentFragment
} else {
$loaded = $this->loadMarkup($markup);
}
if ($loaded) {
// $this->document->formatOutput = true;
$this->document->preserveWhiteSpace = true;
$this->xpath = new DOMXPath($this->document);
$this->afterMarkupLoad();
return true;
// remember last loaded document
// return phpQuery::selectDocument($id);
}
return false;
}
protected function afterMarkupLoad() {
if ($this->isXHTML) {
$this->xpath->registerNamespace("html", "http://www.w3.org/1999/xhtml");
}
}
protected function loadMarkup($markup) {
$loaded = false;
if ($this->contentType) {
self::debug("Load markup for content type {$this->contentType}");
// content determined by contentType
list($contentType, $charset) = $this->contentTypeToArray($this->contentType);
switch($contentType) {
case 'text/html':
phpQuery::debug("Loading HTML, content type '{$this->contentType}'");
$loaded = $this->loadMarkupHTML($markup, $charset);
break;
case 'text/xml':
case 'application/xhtml+xml':
phpQuery::debug("Loading XML, content type '{$this->contentType}'");
$loaded = $this->loadMarkupXML($markup, $charset);
break;
default:
// for feeds or anything that sometimes doesn't use text/xml
if (strpos('xml', $this->contentType) !== false) {
phpQuery::debug("Loading XML, content type '{$this->contentType}'");
$loaded = $this->loadMarkupXML($markup, $charset);
} else
phpQuery::debug("Could not determine document type from content type '{$this->contentType}'");
}
} else {
// content type autodetection
if ($this->isXML($markup)) {
phpQuery::debug("Loading XML, isXML() == true");
$loaded = $this->loadMarkupXML($markup);
if (! $loaded && $this->isXHTML) {
phpQuery::debug('Loading as XML failed, trying to load as HTML, isXHTML == true');
$loaded = $this->loadMarkupHTML($markup);
}
} else {
phpQuery::debug("Loading HTML, isXML() == false");
$loaded = $this->loadMarkupHTML($markup);
}
}
return $loaded;
}
protected function loadMarkupReset() {
$this->isXML = $this->isXHTML = $this->isHTML = false;
}
protected function documentCreate($charset, $version = '1.0') {
if (! $version)
$version = '1.0';
$this->document = new DOMDocument($version, $charset);
$this->charset = $this->document->encoding;
// $this->document->encoding = $charset;
$this->document->formatOutput = true;
$this->document->preserveWhiteSpace = true;
}
protected function loadMarkupHTML($markup, $requestedCharset = null) {
if (phpQuery::$debug)
phpQuery::debug('Full markup load (HTML): '.substr($markup, 0, 250));
$this->loadMarkupReset();
$this->isHTML = true;
if (!isset($this->isDocumentFragment))
$this->isDocumentFragment = self::isDocumentFragmentHTML($markup);
$charset = null;
$documentCharset = $this->charsetFromHTML($markup);
$addDocumentCharset = false;
if ($documentCharset) {
$charset = $documentCharset;
$markup = $this->charsetFixHTML($markup);
} else if ($requestedCharset) {
$charset = $requestedCharset;
}
if (! $charset)
$charset = phpQuery::$defaultCharset;
// HTTP 1.1 says that the default charset is ISO-8859-1
// @see http://www.w3.org/International/O-HTTP-charset
if (! $documentCharset) {
$documentCharset = 'ISO-8859-1';
$addDocumentCharset = true;
}
// Should be careful here, still need 'magic encoding detection' since lots of pages have other 'default encoding'
// Worse, some pages can have mixed encodings... we'll try not to worry about that
$requestedCharset = strtoupper($requestedCharset);
$documentCharset = strtoupper($documentCharset);
phpQuery::debug("DOC: $documentCharset REQ: $requestedCharset");
if ($requestedCharset && $documentCharset && $requestedCharset !== $documentCharset) {
phpQuery::debug("CHARSET CONVERT");
// Document Encoding Conversion
// http://code.google.com/p/phpquery/issues/detail?id=86
if (function_exists('mb_detect_encoding')) {
$possibleCharsets = array($documentCharset, $requestedCharset, 'AUTO');
$docEncoding = mb_detect_encoding($markup, implode(', ', $possibleCharsets));
if (! $docEncoding)
$docEncoding = $documentCharset; // ok trust the document
phpQuery::debug("DETECTED '$docEncoding'");
// Detected does not match what document says...
if ($docEncoding !== $documentCharset) {
// Tricky..
}
if ($docEncoding !== $requestedCharset) {
phpQuery::debug("CONVERT $docEncoding => $requestedCharset");
$markup = mb_convert_encoding($markup, $requestedCharset, $docEncoding);
$markup = $this->charsetAppendToHTML($markup, $requestedCharset);
$charset = $requestedCharset;
}
} else {
phpQuery::debug("TODO: charset conversion without mbstring...");
}
}
$return = false;
if ($this->isDocumentFragment) {
phpQuery::debug("Full markup load (HTML), DocumentFragment detected, using charset '$charset'");
$return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
} else {
if ($addDocumentCharset) {
phpQuery::debug("Full markup load (HTML), appending charset: '$charset'");
$markup = $this->charsetAppendToHTML($markup, $charset);
}
phpQuery::debug("Full markup load (HTML), documentCreate('$charset')");
$this->documentCreate($charset);
$return = phpQuery::$debug === 2
? $this->document->loadHTML($markup)
: @$this->document->loadHTML($markup);
if ($return)
$this->root = $this->document;
}
if ($return && ! $this->contentType)
$this->contentType = 'text/html';
return $return;
}
protected function loadMarkupXML($markup, $requestedCharset = null) {
if (phpQuery::$debug)
phpQuery::debug('Full markup load (XML): '.substr($markup, 0, 250));
$this->loadMarkupReset();
$this->isXML = true;
// check agains XHTML in contentType or markup
$isContentTypeXHTML = $this->isXHTML();
$isMarkupXHTML = $this->isXHTML($markup);
if ($isContentTypeXHTML || $isMarkupXHTML) {
self::debug('Full markup load (XML), XHTML detected');
$this->isXHTML = true;
}
// determine document fragment
if (! isset($this->isDocumentFragment))
$this->isDocumentFragment = $this->isXHTML
? self::isDocumentFragmentXHTML($markup)
: self::isDocumentFragmentXML($markup);
// this charset will be used
$charset = null;
// charset from XML declaration @var string
$documentCharset = $this->charsetFromXML($markup);
if (! $documentCharset) {
if ($this->isXHTML) {
// this is XHTML, try to get charset from content-type meta header
$documentCharset = $this->charsetFromHTML($markup);
if ($documentCharset) {
phpQuery::debug("Full markup load (XML), appending XHTML charset '$documentCharset'");
$this->charsetAppendToXML($markup, $documentCharset);
$charset = $documentCharset;
}
}
if (! $documentCharset) {
// if still no document charset...
$charset = $requestedCharset;
}
} else if ($requestedCharset) {
$charset = $requestedCharset;
}
if (! $charset) {
$charset = phpQuery::$defaultCharset;
}
if ($requestedCharset && $documentCharset && $requestedCharset != $documentCharset) {
// TODO place for charset conversion
// $charset = $requestedCharset;
}
$return = false;
if ($this->isDocumentFragment) {
phpQuery::debug("Full markup load (XML), DocumentFragment detected, using charset '$charset'");
$return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
} else {
// FIXME ???
if ($isContentTypeXHTML && ! $isMarkupXHTML)
if (! $documentCharset) {
phpQuery::debug("Full markup load (XML), appending charset '$charset'");
$markup = $this->charsetAppendToXML($markup, $charset);
}
// see http://pl2.php.net/manual/en/book.dom.php#78929
// LIBXML_DTDLOAD (>= PHP 5.1)
// does XML ctalogues works with LIBXML_NONET
// $this->document->resolveExternals = true;
// TODO test LIBXML_COMPACT for performance improvement
// create document
$this->documentCreate($charset);
if (phpversion() < 5.1) {
$this->document->resolveExternals = true;
$return = phpQuery::$debug === 2
? $this->document->loadXML($markup)
: @$this->document->loadXML($markup);
} else {
/** @link http://pl2.php.net/manual/en/libxml.constants.php */
$libxmlStatic = phpQuery::$debug === 2
? LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET
: LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOERROR;
$return = $this->document->loadXML($markup, $libxmlStatic);
// if (! $return)
// $return = $this->document->loadHTML($markup);
}
if ($return)
$this->root = $this->document;
}
if ($return) {
if (! $this->contentType) {
if ($this->isXHTML)
$this->contentType = 'application/xhtml+xml';
else
$this->contentType = 'text/xml';
}
return $return;
} else {
throw new Exception("Error loading XML markup");
}
}
protected function isXHTML($markup = null) {
if (! isset($markup)) {
return strpos($this->contentType, 'xhtml') !== false;
}
// XXX ok ?
return strpos($markup, "<!DOCTYPE html") !== false;
// return stripos($doctype, 'xhtml') !== false;
// $doctype = isset($dom->doctype) && is_object($dom->doctype)
// ? $dom->doctype->publicId
// : self::$defaultDoctype;
}
protected function isXML($markup) {
// return strpos($markup, '<?xml') !== false && stripos($markup, 'xhtml') === false;
return strpos(substr($markup, 0, 100), '<'.'?xml') !== false;
}
protected function contentTypeToArray($contentType) {
$matches = explode(';', trim(strtolower($contentType)));
if (isset($matches[1])) {
$matches[1] = explode('=', $matches[1]);
// strip 'charset='
$matches[1] = isset($matches[1][1]) && trim($matches[1][1])
? $matches[1][1]
: $matches[1][0];
} else
$matches[1] = null;
return $matches;
}
/**
*
* @param $markup
* @return array contentType, charset
*/
protected function contentTypeFromHTML($markup) {
$matches = array();
// find meta tag
preg_match('@<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
$markup, $matches
);
if (! isset($matches[0]))
return array(null, null);
// get attr 'content'
preg_match('@content\\s*=\\s*(["|\'])(.+?)\\1@', $matches[0], $matches);
if (! isset($matches[0]))
return array(null, null);
return $this->contentTypeToArray($matches[2]);
}
protected function charsetFromHTML($markup) {
$contentType = $this->contentTypeFromHTML($markup);
return $contentType[1];
}
protected function charsetFromXML($markup) {
$matches;
// find declaration
preg_match('@<'.'?xml[^>]+encoding\\s*=\\s*(["|\'])(.*?)\\1@i',
$markup, $matches
);
return isset($matches[2])
? strtolower($matches[2])
: null;
}
/**
* Repositions meta[type=charset] at the start of head. Bypasses DOMDocument bug.
*
* @link http://code.google.com/p/phpquery/issues/detail?id=80
* @param $html
*/
protected function charsetFixHTML($markup) {
$matches = array();
// find meta tag
preg_match('@\s*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
$markup, $matches, PREG_OFFSET_CAPTURE
);
if (! isset($matches[0]))
return;
$metaContentType = $matches[0][0];
$markup = substr($markup, 0, $matches[0][1])
.substr($markup, $matches[0][1]+strlen($metaContentType));
$headStart = stripos($markup, '<head>');
$markup = substr($markup, 0, $headStart+6).$metaContentType
.substr($markup, $headStart+6);
return $markup;
}
protected function charsetAppendToHTML($html, $charset, $xhtml = false) {
// remove existing meta[type=content-type]
$html = preg_replace('@\s*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i', '', $html);
$meta = '<meta http-equiv="Content-Type" content="text/html;charset='
.$charset.'" '
.($xhtml ? '/' : '')
.'>';
if (strpos($html, '<head') === false) {
if (strpos($hltml, '<html') === false) {
return $meta.$html;
} else {
return preg_replace(
'@<html(.*?)(?(?<!\?)>)@s',
"<html\\1><head>{$meta}</head>",
$html
);
}
} else {
return preg_replace(
'@<head(.*?)(?(?<!\?)>)@s',
'<head\\1>'.$meta,
$html
);
}
}
protected function charsetAppendToXML($markup, $charset) {
$declaration = '<'.'?xml version="1.0" encoding="'.$charset.'"?'.'>';
return $declaration.$markup;
}
public static function isDocumentFragmentHTML($markup) {
return stripos($markup, '<html') === false && stripos($markup, '<!doctype') === false;
}
public static function isDocumentFragmentXML($markup) {
return stripos($markup, '<'.'?xml') === false;
}
public static function isDocumentFragmentXHTML($markup) {
return self::isDocumentFragmentHTML($markup);
}
public function importAttr($value) {
// TODO
}
/**
*
* @param $source
* @param $target
* @param $sourceCharset
* @return array Array of imported nodes.
*/
public function import($source, $sourceCharset = null) {
// TODO charset conversions
$return = array();
if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
$source = array($source);
// if (is_array($source)) {
// foreach($source as $node) {
// if (is_string($node)) {
// // string markup
// $fake = $this->documentFragmentCreate($node, $sourceCharset);
// if ($fake === false)
// throw new Exception("Error loading documentFragment markup");
// else
// $return = array_merge($return,
// $this->import($fake->root->childNodes)
// );
// } else {
// $return[] = $this->document->importNode($node, true);
// }
// }
// return $return;
// } else {
// // string markup
// $fake = $this->documentFragmentCreate($source, $sourceCharset);
// if ($fake === false)
// throw new Exception("Error loading documentFragment markup");
// else
// return $this->import($fake->root->childNodes);
// }
if (is_array($source) || $source instanceof DOMNODELIST) {
// dom nodes
self::debug('Importing nodes to document');
foreach($source as $node)
$return[] = $this->document->importNode($node, true);
} else {
// string markup
$fake = $this->documentFragmentCreate($source, $sourceCharset);
if ($fake === false)
throw new Exception("Error loading documentFragment markup");
else
return $this->import($fake->root->childNodes);
}
return $return;
}
/**
* Creates new document fragment.
*
* @param $source
* @return DOMDocumentWrapper
*/
protected function documentFragmentCreate($source, $charset = null) {
$fake = new DOMDocumentWrapper();
$fake->contentType = $this->contentType;
$fake->isXML = $this->isXML;
$fake->isHTML = $this->isHTML;
$fake->isXHTML = $this->isXHTML;
$fake->root = $fake->document;
if (! $charset)
$charset = $this->charset;
// $fake->documentCreate($this->charset);
if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
$source = array($source);
if (is_array($source) || $source instanceof DOMNODELIST) {
// dom nodes
// load fake document
if (! $this->documentFragmentLoadMarkup($fake, $charset))
return false;
$nodes = $fake->import($source);
foreach($nodes as $node)
$fake->root->appendChild($node);
} else {
// string markup
$this->documentFragmentLoadMarkup($fake, $charset, $source);
}
return $fake;
}
/**
*
* @param $document DOMDocumentWrapper
* @param $markup
* @return $document
*/
private function documentFragmentLoadMarkup($fragment, $charset, $markup = null) {
// TODO error handling
// TODO copy doctype
// tempolary turn off
$fragment->isDocumentFragment = false;
if ($fragment->isXML) {
if ($fragment->isXHTML) {
// add FAKE element to set default namespace
$fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?>'
.'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
.'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
.'<fake xmlns="http://www.w3.org/1999/xhtml">'.$markup.'</fake>');
$fragment->root = $fragment->document->firstChild->nextSibling;
} else {
$fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?><fake>'.$markup.'</fake>');
$fragment->root = $fragment->document->firstChild;
}
} else {
$markup2 = phpQuery::$defaultDoctype.'<html><head><meta http-equiv="Content-Type" content="text/html;charset='
.$charset.'"></head>';
$noBody = strpos($markup, '<body') === false;
if ($noBody)
$markup2 .= '<body>';
$markup2 .= $markup;
if ($noBody)
$markup2 .= '</body>';
$markup2 .= '</html>';
$fragment->loadMarkupHTML($markup2);
// TODO resolv body tag merging issue
$fragment->root = $noBody
? $fragment->document->firstChild->nextSibling->firstChild->nextSibling
: $fragment->document->firstChild->nextSibling->firstChild->nextSibling;
}
if (! $fragment->root)
return false;
$fragment->isDocumentFragment = true;
return true;
}
protected function documentFragmentToMarkup($fragment) {
phpQuery::debug('documentFragmentToMarkup');
$tmp = $fragment->isDocumentFragment;
$fragment->isDocumentFragment = false;
$markup = $fragment->markup();
if ($fragment->isXML) {
$markup = substr($markup, 0, strrpos($markup, '</fake>'));
if ($fragment->isXHTML) {
$markup = substr($markup, strpos($markup, '<fake')+43);
} else {
$markup = substr($markup, strpos($markup, '<fake>')+6);
}
} else {
$markup = substr($markup, strpos($markup, '<body>')+6);
$markup = substr($markup, 0, strrpos($markup, '</body>'));
}
$fragment->isDocumentFragment = $tmp;
if (phpQuery::$debug)
phpQuery::debug('documentFragmentToMarkup: '.substr($markup, 0, 150));
return $markup;
}
/**
* Return document markup, starting with optional $nodes as root.
*
* @param $nodes DOMNode|DOMNodeList
* @return string
*/
public function markup($nodes = null, $innerMarkup = false) {
if (isset($nodes) && count($nodes) == 1 && $nodes[0] instanceof DOMDOCUMENT)
$nodes = null;
if (isset($nodes)) {
$markup = '';
if (!is_array($nodes) && !($nodes instanceof DOMNODELIST) )
$nodes = array($nodes);
if ($this->isDocumentFragment && ! $innerMarkup)
foreach($nodes as $i => $node)
if ($node->isSameNode($this->root)) {
// var_dump($node);
$nodes = array_slice($nodes, 0, $i)
+ phpQuery::DOMNodeListToArray($node->childNodes)
+ array_slice($nodes, $i+1);
}
if ($this->isXML && ! $innerMarkup) {
self::debug("Getting outerXML with charset '{$this->charset}'");
// we need outerXML, so we can benefit from
// $node param support in saveXML()
foreach($nodes as $node)
$markup .= $this->document->saveXML($node);
} else {
$loop = array();
if ($innerMarkup)
foreach($nodes as $node) {
if ($node->childNodes)
foreach($node->childNodes as $child)
$loop[] = $child;
else
$loop[] = $node;
}
else
$loop = $nodes;
self::debug("Getting markup, moving selected nodes (".count($loop).") to new DocumentFragment");
$fake = $this->documentFragmentCreate($loop);
$markup = $this->documentFragmentToMarkup($fake);
}
if ($this->isXHTML) {
self::debug("Fixing XHTML");
$markup = self::markupFixXHTML($markup);
}
self::debug("Markup: ".substr($markup, 0, 250));
return $markup;
} else {
if ($this->isDocumentFragment) {
// documentFragment, html only...
self::debug("Getting markup, DocumentFragment detected");
// return $this->markup(
//// $this->document->getElementsByTagName('body')->item(0)
// $this->document->root, true
// );
$markup = $this->documentFragmentToMarkup($this);
// no need for markupFixXHTML, as it's done thought markup($nodes) method
return $markup;
} else {
self::debug("Getting markup (".($this->isXML?'XML':'HTML')."), final with charset '{$this->charset}'");
$markup = $this->isXML
? $this->document->saveXML()
: $this->document->saveHTML();
if ($this->isXHTML) {
self::debug("Fixing XHTML");
$markup = self::markupFixXHTML($markup);
}
self::debug("Markup: ".substr($markup, 0, 250));
return $markup;
}
}
}
protected static function markupFixXHTML($markup) {
$markup = self::expandEmptyTag('script', $markup);
$markup = self::expandEmptyTag('select', $markup);
$markup = self::expandEmptyTag('textarea', $markup);
return $markup;
}
public static function debug($text) {
phpQuery::debug($text);
}
/**
* expandEmptyTag
*
* @param $tag
* @param $xml
* @return unknown_type
* @author mjaque at ilkebenson dot com
* @link http://php.net/manual/en/domdocument.savehtml.php#81256
*/
public static function expandEmptyTag($tag, $xml){
$indice = 0;
while ($indice< strlen($xml)){
$pos = strpos($xml, "<$tag ", $indice);
if ($pos){
$posCierre = strpos($xml, ">", $pos);
if ($xml[$posCierre-1] == "/"){
$xml = substr_replace($xml, "></$tag>", $posCierre-1, 2);
}
$indice = $posCierre;
}
else break;
}
return $xml;
}
}
\ No newline at end of file
... ...
<?php
/**
* DOMEvent class.
*
* Based on
* @link http://developer.mozilla.org/En/DOM:event
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
* @todo implement ArrayAccess ?
*/
class DOMEvent {
/**
* Returns a boolean indicating whether the event bubbles up through the DOM or not.
*
* @var unknown_type
*/
public $bubbles = true;
/**
* Returns a boolean indicating whether the event is cancelable.
*
* @var unknown_type
*/
public $cancelable = true;
/**
* Returns a reference to the currently registered target for the event.
*
* @var unknown_type
*/
public $currentTarget;
/**
* Returns detail about the event, depending on the type of event.
*
* @var unknown_type
* @link http://developer.mozilla.org/en/DOM/event.detail
*/
public $detail; // ???
/**
* Used to indicate which phase of the event flow is currently being evaluated.
*
* NOT IMPLEMENTED
*
* @var unknown_type
* @link http://developer.mozilla.org/en/DOM/event.eventPhase
*/
public $eventPhase; // ???
/**
* The explicit original target of the event (Mozilla-specific).
*
* NOT IMPLEMENTED
*
* @var unknown_type
*/
public $explicitOriginalTarget; // moz only
/**
* The original target of the event, before any retargetings (Mozilla-specific).
*
* NOT IMPLEMENTED
*
* @var unknown_type
*/
public $originalTarget; // moz only
/**
* Identifies a secondary target for the event.
*
* @var unknown_type
*/
public $relatedTarget;
/**
* Returns a reference to the target to which the event was originally dispatched.
*
* @var unknown_type
*/
public $target;
/**
* Returns the time that the event was created.
*
* @var unknown_type
*/
public $timeStamp;
/**
* Returns the name of the event (case-insensitive).
*/
public $type;
public $runDefault = true;
public $data = null;
public function __construct($data) {
foreach($data as $k => $v) {
$this->$k = $v;
}
if (! $this->timeStamp)
$this->timeStamp = time();
}
/**
* Cancels the event (if it is cancelable).
*
*/
public function preventDefault() {
$this->runDefault = false;
}
/**
* Stops the propagation of events further along in the DOM.
*
*/
public function stopPropagation() {
$this->bubbles = false;
}
}
... ...
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
/**
* @category Zend
* @package Zend
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Exception extends Exception
{}
... ...