作者 李小龙

C端客户源码版项目

要显示太多修改。

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

  1 +root = true
  2 +
  3 +[*]
  4 +charset = utf-8
  5 +end_of_line = lf
  6 +insert_final_newline = true
  7 +indent_style = space
  8 +indent_size = 4
  9 +trim_trailing_whitespace = true
  10 +
  11 +[*.md]
  12 +trim_trailing_whitespace = false
  13 +
  14 +[*.{yml,yaml}]
  15 +indent_size = 2
  16 +
  17 +[docker-compose.yml]
  18 +indent_size = 4
  1 +APP_NAME=Laravel
  2 +APP_ENV=local
  3 +APP_KEY=
  4 +APP_DEBUG=true
  5 +APP_URL=http://localhost
  6 +
  7 +LOG_CHANNEL=stack
  8 +LOG_DEPRECATIONS_CHANNEL=null
  9 +LOG_LEVEL=debug
  10 +
  11 +DB_CONNECTION=mysql
  12 +DB_HOST=127.0.0.1
  13 +DB_PORT=3306
  14 +DB_DATABASE=forget
  15 +DB_USERNAME=forget
  16 +DB_PASSWORD=forget
  17 +
  18 +BROADCAST_DRIVER=log
  19 +CACHE_DRIVER=file
  20 +FILESYSTEM_DRIVER=local
  21 +QUEUE_CONNECTION=sync
  22 +SESSION_DRIVER=file
  23 +SESSION_LIFETIME=120
  24 +
  25 +MEMCACHED_HOST=127.0.0.1
  26 +
  27 +REDIS_HOST=127.0.0.1
  28 +REDIS_PASSWORD=null
  29 +REDIS_PORT=6379
  30 +
  31 +MAIL_MAILER=smtp
  32 +MAIL_HOST=mailhog
  33 +MAIL_PORT=1025
  34 +MAIL_USERNAME=null
  35 +MAIL_PASSWORD=null
  36 +MAIL_ENCRYPTION=null
  37 +MAIL_FROM_ADDRESS=null
  38 +MAIL_FROM_NAME="${APP_NAME}"
  39 +
  40 +AWS_ACCESS_KEY_ID=
  41 +AWS_SECRET_ACCESS_KEY=
  42 +AWS_DEFAULT_REGION=us-east-1
  43 +AWS_BUCKET=
  44 +AWS_USE_PATH_STYLE_ENDPOINT=false
  45 +
  46 +PUSHER_APP_ID=
  47 +PUSHER_APP_KEY=
  48 +PUSHER_APP_SECRET=
  49 +PUSHER_APP_CLUSTER=mt1
  50 +
  51 +MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
  52 +MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
  1 +* text=auto
  2 +
  3 +*.blade.php diff=html
  4 +*.css diff=css
  5 +*.html diff=html
  6 +*.md diff=markdown
  7 +*.php diff=php
  8 +
  9 +/.github export-ignore
  10 +CHANGELOG.md export-ignore
  1 +<IfModule mod_rewrite.c>
  2 + <IfModule mod_negotiation.c>
  3 + Options -MultiViews -Indexes
  4 + </IfModule>
  5 +
  6 + RewriteEngine On
  7 +
  8 + # Handle Authorization Header
  9 + RewriteCond %{HTTP:Authorization} .
  10 + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
  11 +
  12 + # Redirect Trailing Slashes If Not A Folder...
  13 + RewriteCond %{REQUEST_FILENAME} !-d
  14 + RewriteCond %{REQUEST_URI} (.+)/$
  15 + RewriteRule ^ %1 [L,R=301]
  16 +
  17 + # Send Requests To Front Controller...
  18 + RewriteCond %{REQUEST_FILENAME} !-d
  19 + RewriteCond %{REQUEST_FILENAME} !-f
  20 + RewriteRule ^ index.php [L]
  21 +</IfModule>
  1 +php:
  2 + preset: laravel
  3 + version: 8
  4 + disabled:
  5 + - no_unused_imports
  6 + finder:
  7 + not-name:
  8 + - index.php
  9 + - server.php
  10 +js:
  11 + finder:
  12 + not-name:
  13 + - webpack.mix.js
  14 +css: true
  1 +<html>
  2 +<head><title>404 Not Found</title></head>
  3 +<body>
  4 +<center><h1>404 Not Found</h1></center>
  5 +<hr><center>nginx</center>
  6 +</body>
  7 +</html>
  1 +<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>
  2 +
  3 +<p align="center">
  4 +<a href="https://travis-ci.org/laravel/framework"><img src="https://travis-ci.org/laravel/framework.svg" alt="Build Status"></a>
  5 +<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
  6 +<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
  7 +<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
  8 +</p>
  9 +
  10 +## About Laravel
  11 +
  12 +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:
  13 +
  14 +- [Simple, fast routing engine](https://laravel.com/docs/routing).
  15 +- [Powerful dependency injection container](https://laravel.com/docs/container).
  16 +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
  17 +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
  18 +- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
  19 +- [Robust background job processing](https://laravel.com/docs/queues).
  20 +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
  21 +
  22 +Laravel is accessible, powerful, and provides tools required for large, robust applications.
  23 +
  24 +## Learning Laravel
  25 +
  26 +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.
  27 +
  28 +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.
  29 +
  30 +## Laravel Sponsors
  31 +
  32 +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).
  33 +
  34 +### Premium Partners
  35 +
  36 +- **[Vehikl](https://vehikl.com/)**
  37 +- **[Tighten Co.](https://tighten.co)**
  38 +- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
  39 +- **[64 Robots](https://64robots.com)**
  40 +- **[Cubet Techno Labs](https://cubettech.com)**
  41 +- **[Cyber-Duck](https://cyber-duck.co.uk)**
  42 +- **[Many](https://www.many.co.uk)**
  43 +- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
  44 +- **[DevSquad](https://devsquad.com)**
  45 +- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
  46 +- **[OP.GG](https://op.gg)**
  47 +- **[WebReinvent](https://webreinvent.com/?utm_source=laravel&utm_medium=github&utm_campaign=patreon-sponsors)**
  48 +- **[Lendio](https://lendio.com)**
  49 +
  50 +## Contributing
  51 +
  52 +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
  53 +
  54 +## Code of Conduct
  55 +
  56 +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).
  57 +
  58 +## Security Vulnerabilities
  59 +
  60 +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.
  61 +
  62 +## License
  63 +
  64 +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
  1 +<?php
  2 +
  3 +namespace App\Console;
  4 +
  5 +use Illuminate\Console\Scheduling\Schedule;
  6 +use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
  7 +
  8 +class Kernel extends ConsoleKernel
  9 +{
  10 + /**
  11 + * Define the application's command schedule.
  12 + *
  13 + * @param \Illuminate\Console\Scheduling\Schedule $schedule
  14 + * @return void
  15 + */
  16 + protected function schedule(Schedule $schedule)
  17 + {
  18 + }
  19 +
  20 + /**
  21 + * Register the commands for the application.
  22 + *
  23 + * @return void
  24 + */
  25 + protected function commands()
  26 + {
  27 + $this->load(__DIR__.'/Commands');
  28 +
  29 + require base_path('routes/console.php');
  30 + }
  31 +}
  1 +<?php
  2 +
  3 +namespace App\Exceptions;
  4 +
  5 +use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
  6 +use Throwable;
  7 +
  8 +class Handler extends ExceptionHandler
  9 +{
  10 + /**
  11 + * A list of the exception types that are not reported.
  12 + *
  13 + * @var array<int, class-string<Throwable>>
  14 + */
  15 + protected $dontReport = [
  16 + //
  17 + ];
  18 +
  19 + /**
  20 + * A list of the inputs that are never flashed for validation exceptions.
  21 + *
  22 + * @var array<int, string>
  23 + */
  24 + protected $dontFlash = [
  25 + 'current_password',
  26 + 'password',
  27 + 'password_confirmation',
  28 + ];
  29 +
  30 + /**
  31 + * Register the exception handling callbacks for the application.
  32 + *
  33 + * @return void
  34 + */
  35 + public function register()
  36 + {
  37 + $this->reportable(function (Throwable $e) {
  38 + //
  39 + });
  40 + }
  41 +}
  1 +<?php
  2 +
  3 +namespace App\Exceptions;
  4 +
  5 +use Exception;
  6 +
  7 +/**
  8 + *
  9 + * Class TipException
  10 + * @package App\Exceptions
  11 + * @author zbj
  12 + * @date 2023/12/12
  13 + */
  14 +class TipException extends Exception
  15 +{
  16 +
  17 +}
  1 +<?php
  2 +
  3 +
  4 +namespace App\Helper;
  5 +
  6 +
  7 +class Str extends \Illuminate\Support\Str
  8 +{
  9 +
  10 +
  11 +}
  1 +<?php
  2 +
  3 +
  4 +namespace App\Helper;
  5 +
  6 +use Illuminate\Support\Facades\Http;
  7 +
  8 +
  9 +
  10 +class Translate
  11 +{
  12 +
  13 +
  14 +}
  1 +<?php
  2 +
  3 +
  4 +
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2023/10/25
  6 + * Time: 14:38
  7 + */
  8 +
  9 +namespace App\Http\Controllers\Api;
  10 +
  11 +use App\Console\Commands\CosService;
  12 +use App\Exceptions\TipException;
  13 +use App\Http\Controllers\Controller;
  14 +use App\Models\Project\Project;
  15 +use App\Models\SyncSubmitTask\SyncSubmitTask;
  16 +use App\Models\WebSetting\WebSettingFormBack;
  17 +use Illuminate\Http\Request;
  18 +use Illuminate\Support\Facades\Cache;
  19 +
  20 +/**
  21 + * Class InquiryController
  22 + * @package App\Http\Controllers\Api
  23 + */
  24 +class InquiryController extends Controller
  25 +{
  26 + /**
  27 + * C端表单提交 询盘信息
  28 + * @param Request $request
  29 + * @return mixed
  30 + */
  31 + public function inquiry(Request $request)
  32 + {
  33 + $data = $request->all();
  34 +
  35 + $black_ips = ['203.86.233.27', '37.19.221.202'];
  36 + if(in_array(request()->getClientIp(), $black_ips)){
  37 + $this->responseA();
  38 + }
  39 +
  40 +
  41 + @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);
  42 +
  43 + //同一ip 5秒内超过5次 限制1小时
  44 + $ip = $request->ip();
  45 + $lock_key = 'lock_ip_' . $ip;
  46 + $rate_key = 'rate_limit:' . $ip;
  47 + if(Cache::has($lock_key)){
  48 + $this->responseA();
  49 + }
  50 + if (Cache::has($rate_key)) {
  51 + $count = Cache::get($rate_key);
  52 + if ($count >= 5) {
  53 + Cache::put($lock_key, 1, 3600);
  54 + $this->responseA();
  55 + }
  56 + Cache::increment($rate_key);
  57 + } else {
  58 + Cache::put($rate_key, 1, 5);
  59 + }
  60 +
  61 + try {
  62 + $files = $request->allFiles();
  63 + foreach ($files as $key => $file) {
  64 + $cos = new CosService();
  65 + $fileinfo = $cos->checkFile($file);
  66 + $fileName = uniqid().rand(10000,99999).'.'.$file->getClientOriginalExtension();
  67 + $path = $cos->uploadFile($file, '/inquiry/'. date('Ymd'), $fileName);
  68 + $data[$key] = [
  69 + 'path' => $path,
  70 + 'original_name' => $fileinfo['name'],
  71 + ];
  72 + }
  73 + }catch (TipException $e){
  74 + $this->responseA([], 400, $e->getMessage());
  75 + }catch (\Exception $e){
  76 + $this->responseA([], 400, 'File upload failed');
  77 + }
  78 +
  79 + //异步处理
  80 + if(!SyncSubmitTask::addTask(SyncSubmitTask::TYPE_INQUIRY, $data)){
  81 + $this->responseA([], 400, 'error');
  82 + }
  83 +
  84 + $this->responseA($this->inquiryResult());
  85 + }
  86 +
  87 + /**
  88 + * 收集邮箱或者手机号等其他信息
  89 + * @param Request $request
  90 + * @return mixed
  91 + */
  92 + public function inquiryOtherInfo(Request $request)
  93 + {
  94 + return $this->inquiry($request);
  95 + }
  96 +
  97 + /**
  98 + * 起点表单询盘
  99 + * @param Request $request
  100 + * @return mixed
  101 + * @author zbj
  102 + * @date 2023/12/8
  103 + */
  104 + public function inquiryQd(Request $request){
  105 + $data = $request->post();
  106 + @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);
  107 +
  108 + //Name,Email,Phone,Message,submit_ip,submit_time,refer
  109 + $submit_ip = $data['submit_ip'] ?? '';
  110 + $submit_time = $data['submit_time'] ?? '';
  111 + $refer = $data['refer'] ?? '';
  112 + unset($data['submit_ip']);
  113 + unset($data['submit_time']);
  114 + unset($data['refer']);
  115 +
  116 + //异步处理
  117 + if(!SyncSubmitTask::addTask(SyncSubmitTask::TYPE_INQUIRY, $data, $submit_ip, $refer, $submit_time)){
  118 + $this->responseA([], 400, 'error');
  119 + }
  120 + $this->responseA($this->inquiryResult());
  121 + }
  122 +
  123 + /**
  124 + * @return mixed|string[]
  125 + * @author zbj
  126 + * @date 2023/12/4
  127 + */
  128 + protected function inquiryResult(){
  129 + $domain = request()->getHost();
  130 + $cache_key = 'inquiry_form_back_' . $domain;
  131 + $result = Cache::get($cache_key);
  132 + if(!$result){
  133 + $result = [
  134 + 'message' => "",
  135 + 'url' => ""
  136 + ];
  137 + $projectDomain = Project::getProjectByDomain($domain);
  138 + $webFormBack = WebSettingFormBack::where("project_id", $projectDomain['project_id']??0)->first();
  139 + if (!empty($webFormBack)) {
  140 + $result["message"] = $webFormBack->message ?? "";
  141 + $result["url"] = $webFormBack->url ?? "";
  142 + Cache::put($cache_key, $result, 3600);
  143 + }
  144 + }
  145 +
  146 + return $result;
  147 + }
  148 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2023/09/02
  6 + * Time: 10:36
  7 + */
  8 +namespace App\Http\Controllers\Api;
  9 +
  10 +use App\Http\Controllers\Controller;
  11 +use Illuminate\Http\Exceptions\HttpResponseException;
  12 +use Illuminate\Http\JsonResponse;
  13 +use Illuminate\Http\Request;
  14 +use Illuminate\Support\Facades\Log;
  15 +use ZipArchive;
  16 +
  17 +/**
  18 + * Class NoticeController
  19 + * @package App\Http\Controllers\Api
  20 + */
  21 +class NoticeController extends Controller
  22 +{
  23 + const SUCCESS = 200;
  24 + const ERROR = 400;
  25 + const SERVERERROR = 500;
  26 + protected $header = [];//设置请求头参数
  27 +
  28 + /**
  29 + * @param array $data
  30 + * @param string $message
  31 + * @param int $status
  32 + * @return string
  33 + */
  34 + protected function success($data = [], $message = 'success', $status = 200)
  35 + {
  36 + $array = compact('status', 'message', 'data');
  37 + return json_encode($array, JSON_UNESCAPED_UNICODE);
  38 + }
  39 +
  40 + /**
  41 + * @param int $status
  42 + * @param string $message
  43 + * @param array $data
  44 + * @return string
  45 + */
  46 + protected function error($message = 'error', $status = 400, $data = [])
  47 + {
  48 + $array = compact('status', 'message', 'data');
  49 + return json_encode($array, JSON_UNESCAPED_UNICODE);
  50 + }
  51 +
  52 + /**
  53 + * 响应
  54 + * @param null $msg
  55 + * @param string $code
  56 + * @param array $data
  57 + * @param int $result_code
  58 + * @param string $type
  59 + * @return JsonResponse
  60 + */
  61 + public function response($msg = null,string $code = self::SUCCESS,$data = [],$result_code = 200,$type = 'application/json'): JsonResponse
  62 + {
  63 + $result = [
  64 + 'msg' => $msg,
  65 + 'code' => $code,
  66 + 'data' => $data,
  67 + ];
  68 + $this->header['Content-Type'] = $type;
  69 + $response = response($result,$result_code,$this->header);
  70 + throw new HttpResponseException($response);
  71 + }
  72 +
  73 + /**
  74 + * GET请求
  75 + * @param $url
  76 + * @return mixed
  77 + */
  78 + public function curlGet($url){
  79 + $ch1 = curl_init();
  80 + $timeout = 0;
  81 + curl_setopt($ch1, CURLOPT_URL, $url);
  82 + curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
  83 + curl_setopt($ch1, CURLOPT_ENCODING, '');
  84 + curl_setopt($ch1, CURLOPT_MAXREDIRS, 10);
  85 + curl_setopt($ch1, CURLOPT_HTTPHEADER, array());
  86 + curl_setopt($ch1, CURLOPT_CONNECTTIMEOUT, $timeout);
  87 + curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, FALSE);
  88 + curl_setopt($ch1, CURLOPT_SSL_VERIFYHOST, FALSE);
  89 + curl_setopt($ch1, CURLOPT_FOLLOWLOCATION, true);
  90 + curl_setopt($ch1, CURLOPT_CUSTOMREQUEST, 'GET');
  91 + curl_setopt($ch1, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  92 + $access_txt = curl_exec($ch1);
  93 + curl_close($ch1);
  94 + return json_decode($access_txt, true);
  95 + }
  96 +
  97 + /**
  98 + * A端上传验证代码
  99 + */
  100 + public function uploadVerifyFile(Request $request): string
  101 + {
  102 + $domain = $request->getHost();
  103 + $files = $request->allFiles();
  104 + foreach ($files as $file) {
  105 + $destinationPath = public_path($domain);
  106 + $file->move($destinationPath, $file->getClientOriginalName());
  107 + $filePath = $destinationPath . '/' . $file->getClientOriginalName();
  108 + @file_put_contents(storage_path('logs/upload_file.log'), date('Y-m-d H:i:s') . " ".$filePath . PHP_EOL, FILE_APPEND);
  109 + }
  110 + return $this->success();
  111 + }
  112 +
  113 + /**
  114 + * A端上传amp站验证代码
  115 + * @param Request $request
  116 + * @return string
  117 + */
  118 + public function uploadAmpVerifyFile(Request $request): string
  119 + {
  120 + $domain = $request->getHost();
  121 +
  122 + $domain_array = parse_url($domain);
  123 + $host = $domain_array['host'] ?? $domain_array['path'];
  124 + $host_array = explode('.',$host);
  125 + if(count($host_array) <= 2){
  126 + array_unshift($host_array,'m');
  127 + }else{
  128 + $host_array[0] = 'm';
  129 + }
  130 + $amp_domain = implode('.',$host_array);
  131 +
  132 + $files = $request->allFiles();
  133 + foreach ($files as $file) {
  134 + $destinationPath = public_path($amp_domain);
  135 + $file->move($destinationPath, $file->getClientOriginalName());
  136 + $filePath = $destinationPath . '/' . $file->getClientOriginalName();
  137 + @file_put_contents(storage_path('logs/upload_file.log'), date('Y-m-d H:i:s') . " ".$filePath . PHP_EOL, FILE_APPEND);
  138 + }
  139 + return $this->success();
  140 + }
  141 +
  142 + /**
  143 + * 获取需要下载的文件url
  144 + * @param Request $request
  145 + * @return string
  146 + */
  147 + public function websiteHtml(Request $request){
  148 +// $domain = $request->getHost();
  149 + //临时测试域名
  150 + $domain = "v6-1x28w.globalso.site";
  151 + $token = env("WEB_SITE_TOKEN");
  152 + $apiUrl = env("API_URL");
  153 + $requestUrl = $apiUrl."?domain=".$domain."&token=".$token;
  154 +
  155 + try {
  156 + $res = $this->curlGet($requestUrl);
  157 + $url = isset($res["data"]["url"]) && !empty($res["data"]["url"]) ? urldecode($res["data"]["url"]) : "";
  158 + if ($res["status"] != self::SUCCESS || $url == ""){
  159 + return $this->error('请求失败!');
  160 + }
  161 + } catch (\Exception $e) {
  162 + return $this->error('请求失败!');
  163 + }
  164 + return $this->websiteHtmlHandle($url);
  165 + }
  166 +
  167 + /**
  168 + * 网站html解压
  169 + * @param $url
  170 + * @return string
  171 + */
  172 + public function websiteHtmlHandle($url)
  173 + {
  174 + $pathInfo = pathinfo($url);
  175 + $extension = $pathInfo['extension'];
  176 + //只允许解压zip格式文件
  177 + if (in_array($extension, ["zip"])) {
  178 + try {
  179 + $targetFile = $this->downLoadFile($url);
  180 + $zip = new ZipArchive();
  181 + if ($zip->open($targetFile) === TRUE) {
  182 + $outputFolder = public_path();
  183 + if (!is_dir($outputFolder)) {
  184 + mkdir($outputFolder, 0777, true);
  185 + }
  186 + // 解压缩文件,保留原文件结构
  187 + $zip->extractTo($outputFolder);
  188 + $zip->close();
  189 + $this->deleteDirectory($targetFile);
  190 + } else {
  191 + return $this->error('解压失败!');
  192 + // 处理打开压缩文件失败的情况
  193 + }
  194 + } catch (\Exception $e) {
  195 + return $this->error($e->getMessage());
  196 + }
  197 + }else{
  198 + return $this->error('不允许解压改格式压缩包!');
  199 + }
  200 + return $this->success();
  201 + }
  202 +
  203 + /**
  204 + * 下载文件(下载到public目录下)
  205 + * @param $url
  206 + * @return string
  207 + */
  208 + public function downLoadFile($url){
  209 + $savePath = public_path();
  210 + if (!file_exists($savePath)) {
  211 + mkdir($savePath, 0777, true);
  212 + }
  213 + $targetFile = $savePath . '/' . basename($url);
  214 + $context = stream_context_create([
  215 + 'http' => [
  216 + '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",
  217 + ],
  218 + ]);
  219 + $fileContent = @file_get_contents($url, false, $context);
  220 + if ($fileContent !== false) {
  221 + file_put_contents($targetFile, $fileContent);
  222 + return $targetFile;
  223 + } else {
  224 + return "";
  225 + }
  226 + }
  227 +
  228 +
  229 + /**
  230 + * 删除文件
  231 + * @param $path
  232 + * @return bool
  233 + */
  234 + public function deleteDirectory($path): bool
  235 + {
  236 + if (!is_dir($path)) {
  237 + try {
  238 + unlink($path);
  239 + } catch (\Exception $e) {
  240 + return true;
  241 + }
  242 + return true;
  243 + }
  244 + $files = array_diff(scandir($path), array('.', '..'));
  245 + foreach ($files as $file) {
  246 + $filePath = $path . '/' . $file;
  247 + if (is_dir($filePath)) {
  248 + $this->deleteDirectory($filePath);
  249 + } else {
  250 + try {
  251 + unlink($filePath);
  252 + } catch (\Exception $e) {
  253 + return true;
  254 + }
  255 + }
  256 + }
  257 + rmdir($path);
  258 + return true;
  259 + }
  260 +
  261 + /**
  262 + * 搜索
  263 + * @param Request $request
  264 + */
  265 + public function search(Request $request)
  266 + {
  267 + $project = $request->get('project');
  268 + $data = $request->all();
  269 +
  270 + //获取搜索参数
  271 + $searchContent = '';
  272 + if (isset($data['s'])) {
  273 + $searchContent = $data['s'];
  274 + }
  275 + if (isset($data['search'])) {
  276 + $searchContent = $data['search'];
  277 + }
  278 +
  279 + $page = 1;
  280 + if (isset($data['page']) && (int)$data['page'] > 1) {
  281 + $page = (int)$data['page'];
  282 + }
  283 +
  284 +// $htmlService = new HtmlService();
  285 +
  286 +
  287 +// return $htmlService->getSearchHtml($project, $data, $searchContent, $page);
  288 + }
  289 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Controllers\Api;
  4 +
  5 +use App\Helper\Str;
  6 +use App\Http\Controllers\Controller;
  7 +use App\Models\Product\Product;
  8 +use App\Models\Product\ProductExtendInfo;
  9 +use App\Services\PageService;
  10 +use Illuminate\Http\Request;
  11 +use Illuminate\Support\Facades\Cache;
  12 +use Illuminate\Support\Facades\Http;
  13 +
  14 +/***
  15 + * 快捷搜索
  16 + * Class SearchController
  17 + * @package App\Http\Controllers\Api
  18 + * @author zbj
  19 + * @date 2024/1/22
  20 + */
  21 +class SearchController extends Controller
  22 +{
  23 + /**
  24 + * C端搜索框快捷搜索
  25 + * @param Request $request
  26 + * @return mixed
  27 + */
  28 + public function index(Request $request)
  29 + {
  30 +
  31 + $limit = $request->input('limit') ?: 5;
  32 + $text = $request->input('text');
  33 + $key = $request->input('key') ?: 'title';
  34 + $key_limit = $request->input('key_limit') ?: 15;
  35 + $project = $request->get('project');
  36 + if (!$text) {
  37 + $this->responseA([]);
  38 + }
  39 +
  40 + //匹配产品标题及产品对应的分类
  41 + $data = $this->searchProduct($text, $limit, $project->id);
  42 +
  43 + //匹配建议字段
  44 + $data['suggestions'] = $this->searchSuggestion($text, $key, $key_limit, $project->id);
  45 +
  46 + $this->responseA($data);
  47 + }
  48 +
  49 +
  50 + /**
  51 + * @param $text
  52 + * @param $limit
  53 + * @return array
  54 + * @author zbj
  55 + * @date 2024/1/22
  56 + */
  57 + protected function searchProduct($text, $limit, $project_id = 0): array
  58 + {
  59 + //匹配产品
  60 + $all_products = Cache::get('product_list_project_' . $project_id);
  61 + if($all_products){
  62 + $products = [];
  63 + foreach ($all_products as $product){
  64 + if(Str::contains($product['title'], $text)){
  65 + $products[] = [
  66 + 'title' => $product['title'],
  67 + 'thumb' => $product['thumb'],
  68 + 'id' => $product['id'],
  69 + 'route' => $product['route'],
  70 + 'category' => $product['category'],
  71 + ];
  72 + if(count($products) >= $limit){
  73 + break;
  74 + }
  75 + }
  76 + }
  77 + }else{
  78 + $products = Product::with('category')->where("title", 'like', $text . '%')
  79 + ->where("status", 1)
  80 + ->orderBy("id", "DESC")
  81 + ->limit($limit)
  82 + ->select('title', 'thumb', 'id', 'route')
  83 + ->get()
  84 + ->toArray();
  85 + }
  86 +
  87 + //对应分类
  88 + $categories = [];
  89 + foreach ($products as &$product) {
  90 + foreach ($product['category'] as $category) {
  91 + $categories[$category['route']] = [
  92 + 'title' => $category['title'],
  93 + 'route' => '/' . $category['route'] . '/',
  94 + ];
  95 + }
  96 + unset($product['id']);
  97 + unset($product['category']);
  98 + $product['thumb'] = app(PageService::class)->getImageUrl(json_decode($product['thumb'], true)['url'] ?? '');
  99 + $product['route'] = '/' . $product['route'] . '/';
  100 + }
  101 +
  102 + return [
  103 + 'products' => $products,
  104 + 'categories' => array_values($categories),
  105 + ];
  106 + }
  107 +
  108 + /**
  109 + * @param $text
  110 + * @param $key
  111 + * @param $key_limit
  112 + * @return array
  113 + * @author zbj
  114 + * @date 2024/1/22
  115 + */
  116 + protected function searchSuggestion($text, $key, $key_limit, $project_id = 0): array
  117 + {
  118 + $columns = Cache::get('product_filed_project_' . $project_id);
  119 + if(!$columns){
  120 + $model = new Product();
  121 + $columns = $model->getConnection()->getSchemaBuilder()->getColumnListing($model->getTable());
  122 + }
  123 +
  124 +
  125 + //产品字段
  126 + if (in_array($key, $columns)) {
  127 + $all_products = Cache::get('product_list_project_' . $project_id);
  128 + if($all_products){
  129 + $suggestions = [];
  130 + foreach ($all_products as $product){
  131 + if(Str::contains($product[$key], $text)){
  132 + $suggestions[] = [
  133 + 'title' => $product[$key],
  134 + 'route' => $product['route'],
  135 + ];
  136 + }
  137 + if(count($suggestions) >= $key_limit){
  138 + break;
  139 + }
  140 + }
  141 + }else{
  142 + //匹配产品
  143 + $suggestions = Product::where("status", 1)
  144 + ->where($key, 'like', $text . '%')
  145 + ->orderBy("id", "DESC")
  146 + ->limit($key_limit)
  147 + ->select($key .' as title', 'route')
  148 + ->get()
  149 + ->toArray();
  150 + }
  151 + } else {
  152 + $all_suggestions = Cache::get('product_extend_project_' . $project_id);
  153 + if($all_suggestions){
  154 + $suggestions = [];
  155 + foreach ($all_suggestions as $suggestion){
  156 + if(Str::contains($suggestion['values'], $text)){
  157 + $suggestions[] = [
  158 + 'values' => $suggestion['values'],
  159 + 'route' => $suggestion['route'],
  160 + ];
  161 + }
  162 + if(count($suggestions) >= $key_limit){
  163 + break;
  164 + }
  165 + }
  166 + }else{
  167 + //扩展字段
  168 + $suggestions = Product::leftJoin('gl_product_extend_info as pei', 'gl_product.id', '=', 'pei.product_id')
  169 + ->where('pei.values', 'like', $text . '%')
  170 + ->where("gl_product.status", 1)
  171 + ->orderBy("gl_product.id", "DESC")
  172 + ->limit($key_limit)
  173 + ->select('pei.values','gl_product.route')
  174 + ->get()
  175 + ->toArray();
  176 + }
  177 + }
  178 +
  179 + foreach ($suggestions as &$suggestion){
  180 + $suggestion['route'] = '/' . $suggestion['route'] . '/';
  181 + }
  182 +
  183 + return $suggestions;
  184 + }
  185 +}
  186 +
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/1/6
  6 + * Time: 16:06
  7 + */
  8 +namespace App\Http\Controllers\Api;
  9 +
  10 +use App\Http\Controllers\Controller;
  11 +use App\Repositories\SyncSubmitRepository;
  12 +use Illuminate\Http\Request;
  13 +use Illuminate\Support\Facades\Validator;
  14 +
  15 +/**
  16 + * Class TrafficController
  17 + * @package App\Http\Controllers\Api
  18 + */
  19 +class TrafficController extends Controller
  20 +{
  21 + /**
  22 + * 特殊访问引流接口
  23 + * @param Request $request
  24 + * @return string
  25 + */
  26 + public function trafficVisit(Request $request)
  27 + {
  28 + //获取参数
  29 + $data = $request->all();
  30 + $ip = $request->input('ip');
  31 + $url = $request->input('url');
  32 + $device_port = intval($request->input('device_port'));
  33 + $referrer_url = $request->input('referrer_url');
  34 + $user_agent = $request->input('user_agent');
  35 +
  36 + // FIXME 些日志 观察数据, 完成以后删除代码
  37 + file_put_contents(storage_path('logs/trafficVisit' . date('Y-m-d') . '.log'), var_export($data, true));
  38 + //验证参数
  39 + $validator = Validator::make($data, [
  40 + 'ip' => 'required',
  41 + 'url' => 'required',
  42 + 'user_agent' => 'required',
  43 + ], [
  44 + 'ip.required' => 'IP不能为空',
  45 + 'url.required' => '访问链接不能为空',
  46 + 'user_agent.required' => '请求头信息user_agent不能为空',
  47 + ]);
  48 + //判断参数是否正确
  49 + if ($validator->fails()) {
  50 + return $this->error($validator->errors()->first());
  51 + }
  52 +
  53 + $result = app(SyncSubmitRepository::class)->trafficVisit($ip, $url, $user_agent, $referrer_url, $device_port, 1);
  54 + return $result ? $this->success() : $this->error();
  55 + }
  56 +
  57 +
  58 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Controllers;
  4 +
  5 +use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
  6 +use Illuminate\Foundation\Bus\DispatchesJobs;
  7 +use Illuminate\Foundation\Validation\ValidatesRequests;
  8 +use Illuminate\Routing\Controller as BaseController;
  9 +
  10 +class Controller extends BaseController
  11 +{
  12 + use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
  13 +
  14 +}
  15 +
  16 +
  1 +<?php
  2 +
  3 +namespace App\Http\Controllers\Cside;
  4 +
  5 +use App\Http\Controllers\Controller;
  6 +use Illuminate\Http\Exceptions\HttpResponseException;
  7 +use Illuminate\Http\JsonResponse;
  8 +
  9 +class BaseController extends Controller
  10 +{
  11 + const SUCCESS = 200;
  12 + const ERROR = 400;
  13 + protected $param = [];//所有请求参数
  14 + protected $token = ''; //token
  15 + protected $request = [];//助手函数
  16 + protected $page = 1;//当前页
  17 + protected $row = 20;//每页条数
  18 + protected $header = [];//设置请求头参数
  19 + protected $order = 'id';
  20 + protected $map = [];//处理后的参数
  21 + protected $uid = 0;
  22 + protected $user = [];//当前登录用户详情
  23 + public function __construct(){
  24 + }
  25 +
  26 + /**
  27 + * 响应
  28 + * @throws HttpResponseException
  29 + */
  30 + public function response($msg = null,string $code = self::SUCCESS,$data = [],$result_code = 200,$type = 'application/json'): JsonResponse
  31 + {
  32 + $result = [
  33 + 'msg' => $msg,
  34 + 'code' => $code,
  35 + 'data' => $data,
  36 + ];
  37 + $this->header['Content-Type'] = $type;
  38 + $this->header['token'] = $this->token;
  39 + $response = response($result,$result_code,$this->header);
  40 + throw new HttpResponseException($response);
  41 + }
  42 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Controllers\Cside\Visit;
  4 +
  5 +use App\Enums\Common\Code;
  6 +use App\Http\Controllers\Cside\BaseController;
  7 +use App\Models\Project\Project;
  8 +use App\Models\SyncSubmitTask\SyncSubmitTask;
  9 +use Illuminate\Http\Request;
  10 +use Illuminate\Support\Facades\Log;
  11 +
  12 +class VisitController extends BaseController
  13 +{
  14 + /**
  15 + * 客户访问埋点接口
  16 + */
  17 + public function customerVisit(Request $request): \Illuminate\Http\JsonResponse
  18 + {
  19 + $data = $request->all();
  20 +
  21 + if ($this->filter($request)){
  22 + $data = $this->visitInfoHandle($data);
  23 + //异步处理
  24 + if(!SyncSubmitTask::addTask(SyncSubmitTask::TYPE_VISIT, $data)){
  25 + $this->responseA([], 400, 'error');
  26 + }
  27 + }
  28 +
  29 + //埋点成功
  30 + return response()->json([
  31 + 'code' => Code::SUCCESS_NUM,
  32 + 'msg' => '客户访问',
  33 + ]);
  34 + }
  35 +
  36 + public function filter($request){
  37 + if($request->getClientIp() == "127.0.0.1"){
  38 + return false;
  39 + }
  40 + //判断是否是爬虫
  41 + $isReptile = $this->isReptile($request);
  42 + if($isReptile){
  43 + return false;
  44 + }
  45 + //是否允许测试环境
  46 + $projectDomain = Project::getProjectByDomain($request->getHost());
  47 + $project = Project::find($projectDomain['project_id']??0);
  48 + if(empty($project)){
  49 + return false;
  50 + }
  51 + // 测试环境返回信息
  52 + if (FALSE !== strpos($request->getHost(), 'globalso.site') && !$project->is_record_test_visit) {
  53 + return false;
  54 + }
  55 + return true;
  56 + }
  57 +
  58 + /**
  59 + * 埋点信息处理
  60 + */
  61 + public function visitInfoHandle($data)
  62 + {
  63 + //referrer
  64 + if(preg_match('/google|facebook|bing|yahoo|youtobe|linkedin|messefrankfurt|yandex|tiktok|twitter|instagram|reddit|telegram|pinterest|tumblr/', $data['referrer_url'])){
  65 + }else if($data['referrer_url'] == null){
  66 + //直访用户
  67 + $data['referrer_url'] = "";
  68 + }else{
  69 + $data['referrer_url'] = "https://www.google.com/";
  70 + }
  71 + return $data;
  72 + }
  73 +
  74 + /**
  75 + * 是否是爬虫访问
  76 + */
  77 + public function isReptile($request): bool
  78 + {
  79 + $agent = $request->header('User-Agent');
  80 + if (!empty($agent)) {
  81 + $spiderSite= array(
  82 + "TencentTraveler",
  83 + "Baiduspider+",
  84 + "BaiduGame",
  85 + "Googlebot",
  86 + "msnbot",
  87 + "Sosospider+",
  88 + "Sogou web spider",
  89 + "ia_archiver",
  90 + "Yahoo! Slurp",
  91 + "YoudaoBot",
  92 + "Yahoo Slurp",
  93 + "MSNBot",
  94 + "Java (Often spam bot)",
  95 + "BaiDuSpider",
  96 + "Voila",
  97 + "Yandex bot",
  98 + "BSpider",
  99 + "twiceler",
  100 + "Sogou Spider",
  101 + "Speedy Spider",
  102 + "Google AdSense",
  103 + "Heritrix",
  104 + "Python-urllib",
  105 + "Alexa (IA Archiver)",
  106 + "Ask",
  107 + "Exabot",
  108 + "Custo",
  109 + "OutfoxBot/YodaoBot",
  110 + "yacy",
  111 + "SurveyBot",
  112 + "legs",
  113 + "lwp-trivial",
  114 + "Nutch",
  115 + "StackRambler",
  116 + "The web archive (IA Archiver)",
  117 + "Perl tool",
  118 + "MJ12bot",
  119 + "Netcraft",
  120 + "MSIECrawler",
  121 + "WGet tools",
  122 + "larbin",
  123 + "Fish search",
  124 + "yandex.com/bots",
  125 + "google.com/bot",
  126 + "bingbot",
  127 + "YandexMobileBot",
  128 + "BingPreview",
  129 + "AhrefsBot",
  130 + "bot"
  131 + );
  132 + $flag = 0;
  133 + foreach($spiderSite as $val) {
  134 + $str = strtolower($val);
  135 + if (strpos($agent, $str) !== false) {
  136 + $flag = 1;
  137 + }
  138 + }
  139 + if($flag == 1){
  140 + return true;
  141 + }else{
  142 + return false;
  143 + }
  144 + } else {
  145 + return false;
  146 + }
  147 + }
  148 +}
  1 +<?php
  2 +
  3 +namespace App\Http;
  4 +
  5 +use Illuminate\Foundation\Http\Kernel as HttpKernel;
  6 +
  7 +class Kernel extends HttpKernel
  8 +{
  9 + /**
  10 + * The application's global HTTP middleware stack.
  11 + *
  12 + * These middleware are run during every request to your application.
  13 + *
  14 + * @var array<int, class-string|string>
  15 + */
  16 + protected $middleware = [
  17 + // \App\Http\Middleware\TrustHosts::class,
  18 + \App\Http\Middleware\TrustProxies::class,
  19 + \Fruitcake\Cors\HandleCors::class,
  20 + \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
  21 + \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
  22 + \App\Http\Middleware\TrimStrings::class,
  23 + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
  24 + ];
  25 +
  26 + /**
  27 + * The application's route middleware groups.
  28 + *
  29 + * @var array<string, array<int, class-string|string>>
  30 + */
  31 + protected $middlewareGroups = [
  32 + 'web' => [
  33 + \App\Http\Middleware\EncryptCookies::class,
  34 + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
  35 + \Illuminate\Session\Middleware\StartSession::class,
  36 + // \Illuminate\Session\Middleware\AuthenticateSession::class,
  37 + \Illuminate\View\Middleware\ShareErrorsFromSession::class,
  38 + \App\Http\Middleware\VerifyCsrfToken::class,
  39 + \Illuminate\Routing\Middleware\SubstituteBindings::class,
  40 +
  41 + ],
  42 +
  43 + 'api' => [
  44 + // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
  45 + //'throttle:1000,1',
  46 + \Illuminate\Routing\Middleware\SubstituteBindings::class,
  47 + \App\Http\Middleware\CorsMiddleware::class,
  48 + ],
  49 + ];
  50 +
  51 + /**
  52 + * The application's route middleware.
  53 + *
  54 + * These middleware may be assigned to groups or used individually.
  55 + *
  56 + * @var array<string, class-string|string>
  57 + */
  58 + protected $routeMiddleware = [
  59 + 'auth' => \App\Http\Middleware\Authenticate::class,
  60 + 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
  61 + 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
  62 + 'can' => \Illuminate\Auth\Middleware\Authorize::class,
  63 + 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
  64 + 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
  65 + 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
  66 + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
  67 + 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
  68 + 'cors' => \App\Http\Middleware\CorsMiddleware::class,
  69 + ];
  70 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Middleware;
  4 +
  5 +use Illuminate\Auth\Middleware\Authenticate as Middleware;
  6 +
  7 +class Authenticate extends Middleware
  8 +{
  9 + /**
  10 + * Get the path the user should be redirected to when they are not authenticated.
  11 + *
  12 + * @param \Illuminate\Http\Request $request
  13 + * @return string|null
  14 + */
  15 + protected function redirectTo($request)
  16 + {
  17 + if (! $request->expectsJson()) {
  18 + return route('login');
  19 + }
  20 + }
  21 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Middleware;
  4 +
  5 +use Closure;
  6 +use Illuminate\Http\Request;
  7 +
  8 +class CorsMiddleware
  9 +{
  10 + /**
  11 + * Handle an incoming request.
  12 + *
  13 + * @param \Illuminate\Http\Request $request
  14 + * @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
  15 + * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
  16 + */
  17 + public function handle(Request $request, Closure $next)
  18 + {
  19 + $response = $next($request);
  20 + $response->header('Access-Control-Allow-Origin', "*");
  21 + $response->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  22 + $response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Authorization');
  23 + if (strtolower($_SERVER['REQUEST_METHOD']) == 'options') {
  24 + exit;
  25 + }
  26 + return $response;
  27 + }
  28 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Middleware;
  4 +
  5 +use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
  6 +
  7 +class EncryptCookies extends Middleware
  8 +{
  9 + /**
  10 + * The names of the cookies that should not be encrypted.
  11 + *
  12 + * @var array<int, string>
  13 + */
  14 + protected $except = [
  15 + //
  16 + ];
  17 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Middleware;
  4 +
  5 +use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
  6 +
  7 +class PreventRequestsDuringMaintenance extends Middleware
  8 +{
  9 + /**
  10 + * The URIs that should be reachable while maintenance mode is enabled.
  11 + *
  12 + * @var array<int, string>
  13 + */
  14 + protected $except = [
  15 + //
  16 + ];
  17 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Middleware;
  4 +
  5 +use App\Providers\RouteServiceProvider;
  6 +use Closure;
  7 +use Illuminate\Http\Request;
  8 +use Illuminate\Support\Facades\Auth;
  9 +
  10 +class RedirectIfAuthenticated
  11 +{
  12 + /**
  13 + * Handle an incoming request.
  14 + *
  15 + * @param \Illuminate\Http\Request $request
  16 + * @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
  17 + * @param string|null ...$guards
  18 + * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
  19 + */
  20 + public function handle(Request $request, Closure $next, ...$guards)
  21 + {
  22 + $guards = empty($guards) ? [null] : $guards;
  23 +
  24 + foreach ($guards as $guard) {
  25 + if (Auth::guard($guard)->check()) {
  26 + return redirect(RouteServiceProvider::HOME);
  27 + }
  28 + }
  29 +
  30 + return $next($request);
  31 + }
  32 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Middleware;
  4 +
  5 +use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
  6 +
  7 +class TrimStrings extends Middleware
  8 +{
  9 + /**
  10 + * The names of the attributes that should not be trimmed.
  11 + *
  12 + * @var array<int, string>
  13 + */
  14 + protected $except = [
  15 + 'current_password',
  16 + 'password',
  17 + 'password_confirmation',
  18 + ];
  19 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Middleware;
  4 +
  5 +use Illuminate\Http\Middleware\TrustHosts as Middleware;
  6 +
  7 +class TrustHosts extends Middleware
  8 +{
  9 + /**
  10 + * Get the host patterns that should be trusted.
  11 + *
  12 + * @return array<int, string|null>
  13 + */
  14 + public function hosts()
  15 + {
  16 + return [
  17 + $this->allSubdomainsOfApplicationUrl(),
  18 + ];
  19 + }
  20 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Middleware;
  4 +
  5 +use Illuminate\Http\Middleware\TrustProxies as Middleware;
  6 +use Illuminate\Http\Request;
  7 +
  8 +class TrustProxies extends Middleware
  9 +{
  10 + /**
  11 + * The trusted proxies for this application.
  12 + *
  13 + * @var array<int, string>|string|null
  14 + */
  15 + protected $proxies;
  16 +
  17 + /**
  18 + * The headers that should be used to detect proxies.
  19 + *
  20 + * @var int
  21 + */
  22 + protected $headers =
  23 + Request::HEADER_X_FORWARDED_FOR |
  24 + Request::HEADER_X_FORWARDED_HOST |
  25 + Request::HEADER_X_FORWARDED_PORT |
  26 + Request::HEADER_X_FORWARDED_PROTO |
  27 + Request::HEADER_X_FORWARDED_AWS_ELB;
  28 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Middleware;
  4 +
  5 +use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
  6 +
  7 +class VerifyCsrfToken extends Middleware
  8 +{
  9 + /**
  10 + * The URIs that should be excluded from CSRF verification.
  11 + *
  12 + * @var array<int, string>
  13 + */
  14 + protected $except = [
  15 + "api/customerVisit",
  16 + "api/inquiry",
  17 + "api/inquiryOtherInfo",
  18 + "api/website_html_handle",
  19 + ];
  20 +}
  1 +<?php
  2 +
  3 +namespace App\Providers;
  4 +
  5 +use Illuminate\Support\ServiceProvider;
  6 +
  7 +class AppServiceProvider extends ServiceProvider
  8 +{
  9 + /**
  10 + * Register any application services.
  11 + *
  12 + * @return void
  13 + */
  14 + public function register()
  15 + {
  16 + //
  17 + }
  18 +
  19 + /**
  20 + * Bootstrap any application services.
  21 + *
  22 + * @return void
  23 + */
  24 + public function boot()
  25 + {
  26 + //
  27 + }
  28 +}
  1 +<?php
  2 +
  3 +namespace App\Providers;
  4 +
  5 +use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
  6 +use Illuminate\Support\Facades\Gate;
  7 +
  8 +class AuthServiceProvider extends ServiceProvider
  9 +{
  10 + /**
  11 + * The policy mappings for the application.
  12 + *
  13 + * @var array<class-string, class-string>
  14 + */
  15 + protected $policies = [
  16 +
  17 + ];
  18 +
  19 + /**
  20 + * Register any authentication / authorization services.
  21 + *
  22 + * @return void
  23 + */
  24 + public function boot()
  25 + {
  26 + $this->registerPolicies();
  27 + }
  28 +}
  1 +<?php
  2 +
  3 +namespace App\Providers;
  4 +
  5 +use Illuminate\Support\Facades\Broadcast;
  6 +use Illuminate\Support\ServiceProvider;
  7 +
  8 +class BroadcastServiceProvider extends ServiceProvider
  9 +{
  10 + /**
  11 + * Bootstrap any application services.
  12 + *
  13 + * @return void
  14 + */
  15 + public function boot()
  16 + {
  17 + Broadcast::routes();
  18 +
  19 + require base_path('routes/channels.php');
  20 + }
  21 +}
  1 +<?php
  2 +
  3 +namespace App\Providers;
  4 +
  5 +use App\Events\UpdateHtml;
  6 +use App\Listeners\UpdateHtmlListener;
  7 +use Illuminate\Auth\Events\Registered;
  8 +use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
  9 +use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
  10 +
  11 +class EventServiceProvider extends ServiceProvider
  12 +{
  13 + /**
  14 + * The event listener mappings for the application.
  15 + *
  16 + * @var array<class-string, array<int, class-string>>
  17 + */
  18 + protected $listen = [
  19 + Registered::class => [
  20 + SendEmailVerificationNotification::class,
  21 + ],
  22 + ];
  23 +
  24 + /**
  25 + * Register any events for your application.
  26 + *
  27 + * @return void
  28 + */
  29 + public function boot()
  30 + {
  31 + //
  32 + }
  33 +}
  1 +<?php
  2 +
  3 +namespace App\Providers;
  4 +
  5 +use Illuminate\Cache\RateLimiting\Limit;
  6 +use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
  7 +use Illuminate\Http\Request;
  8 +use Illuminate\Support\Facades\RateLimiter;
  9 +use Illuminate\Support\Facades\Route;
  10 +
  11 +class RouteServiceProvider extends ServiceProvider
  12 +{
  13 + /**
  14 + * The path to the "home" route for your application.
  15 + *
  16 + * This is used by Laravel authentication to redirect users after login.
  17 + *
  18 + * @var string
  19 + */
  20 + public const HOME = '/home';
  21 +
  22 + /**
  23 + * The controller namespace for the application.
  24 + *
  25 + * When present, controller route declarations will automatically be prefixed with this namespace.
  26 + *
  27 + * @var string|null
  28 + */
  29 + // protected $namespace = 'App\\Http\\Controllers';
  30 +
  31 + /**
  32 + * Define your route model bindings, pattern filters, etc.
  33 + *
  34 + * @return void
  35 + */
  36 + public function boot()
  37 + {
  38 + $sld_prefix = isset($_SERVER['HTTP_HOST']) ? explode('.',$_SERVER['HTTP_HOST'])[0] : "";
  39 + if($sld_prefix == 'm'){
  40 + $this->mapAmpRoutes();
  41 + }else{
  42 + $this->configureRateLimiting();
  43 +
  44 + $this->routes(function () {
  45 + Route::prefix('api')
  46 + ->middleware('api')
  47 + ->namespace($this->namespace)
  48 + ->group(base_path('routes/api.php'));
  49 +
  50 + Route::middleware('web')
  51 + ->namespace($this->namespace)
  52 + ->group(base_path('routes/web.php'));
  53 + });
  54 + }
  55 + }
  56 +
  57 + /**
  58 + * Configure the rate limiters for the application.
  59 + *
  60 + * @return void
  61 + */
  62 + protected function configureRateLimiting()
  63 + {
  64 + RateLimiter::for('api', function (Request $request) {
  65 + return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
  66 + });
  67 + }
  68 +
  69 + /**
  70 + * @author Akun
  71 + * @date 2024/01/26 11:03
  72 + */
  73 + protected function mapAmpRoutes(){
  74 + Route::namespace('App\Http\Controllers\Amp')
  75 + ->group(base_path('routes/amp.php'));
  76 + }
  77 +}
  1 +<?php
  2 +/**
  3 + * phpQuery is a server-side, chainable, CSS3 selector driven
  4 + * Document Object Model (DOM) API based on jQuery JavaScript Library.
  5 + *
  6 + * @version 0.9.5
  7 + * @link http://code.google.com/p/phpquery/
  8 + * @link http://phpquery-library.blogspot.com/
  9 + * @link http://jquery.com/
  10 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  11 + * @license http://www.opensource.org/licenses/mit-license.php MIT License
  12 + * @package phpQuery
  13 + */
  14 +
  15 +// class names for instanceof
  16 +// TODO move them as class constants into phpQuery
  17 +define('DOMDOCUMENT', 'DOMDocument');
  18 +define('DOMELEMENT', 'DOMElement');
  19 +define('DOMNODELIST', 'DOMNodeList');
  20 +define('DOMNODE', 'DOMNode');
  21 +require_once(dirname(__FILE__).'/phpQuery/DOMEvent.php');
  22 +require_once(dirname(__FILE__).'/phpQuery/DOMDocumentWrapper.php');
  23 +require_once(dirname(__FILE__).'/phpQuery/phpQueryEvents.php');
  24 +require_once(dirname(__FILE__).'/phpQuery/Callback.php');
  25 +require_once(dirname(__FILE__).'/phpQuery/phpQueryObject.php');
  26 +require_once(dirname(__FILE__).'/phpQuery/compat/mbstring.php');
  27 +/**
  28 + * Static namespace for phpQuery functions.
  29 + *
  30 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  31 + * @package phpQuery
  32 + */
  33 +abstract class phpQuery {
  34 + /**
  35 + * XXX: Workaround for mbstring problems
  36 + *
  37 + * @var bool
  38 + */
  39 + public static $mbstringSupport = true;
  40 + public static $debug = false;
  41 + public static $documents = array();
  42 + public static $defaultDocumentID = null;
  43 +// public static $defaultDoctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"';
  44 + /**
  45 + * Applies only to HTML.
  46 + *
  47 + * @var unknown_type
  48 + */
  49 + public static $defaultDoctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  50 +"http://www.w3.org/TR/html4/loose.dtd">';
  51 + public static $defaultCharset = 'UTF-8';
  52 + /**
  53 + * Static namespace for plugins.
  54 + *
  55 + * @var object
  56 + */
  57 + public static $plugins = array();
  58 + /**
  59 + * List of loaded plugins.
  60 + *
  61 + * @var unknown_type
  62 + */
  63 + public static $pluginsLoaded = array();
  64 + public static $pluginsMethods = array();
  65 + public static $pluginsStaticMethods = array();
  66 + public static $extendMethods = array();
  67 + /**
  68 + * @TODO implement
  69 + */
  70 + public static $extendStaticMethods = array();
  71 + /**
  72 + * Hosts allowed for AJAX connections.
  73 + * Dot '.' means $_SERVER['HTTP_HOST'] (if any).
  74 + *
  75 + * @var array
  76 + */
  77 + public static $ajaxAllowedHosts = array(
  78 + '.'
  79 + );
  80 + /**
  81 + * AJAX settings.
  82 + *
  83 + * @var array
  84 + * XXX should it be static or not ?
  85 + */
  86 + public static $ajaxSettings = array(
  87 + 'url' => '',//TODO
  88 + 'global' => true,
  89 + 'type' => "GET",
  90 + 'timeout' => null,
  91 + 'contentType' => "application/x-www-form-urlencoded",
  92 + 'processData' => true,
  93 +// 'async' => true,
  94 + 'data' => null,
  95 + 'username' => null,
  96 + 'password' => null,
  97 + 'accepts' => array(
  98 + 'xml' => "application/xml, text/xml",
  99 + 'html' => "text/html",
  100 + 'script' => "text/javascript, application/javascript",
  101 + 'json' => "application/json, text/javascript",
  102 + 'text' => "text/plain",
  103 + '_default' => "*/*"
  104 + )
  105 + );
  106 + public static $lastModified = null;
  107 + public static $active = 0;
  108 + public static $dumpCount = 0;
  109 + /**
  110 + * Multi-purpose function.
  111 + * Use pq() as shortcut.
  112 + *
  113 + * In below examples, $pq is any result of pq(); function.
  114 + *
  115 + * 1. Import markup into existing document (without any attaching):
  116 + * - Import into selected document:
  117 + * pq('<div/>') // DOESNT accept text nodes at beginning of input string !
  118 + * - Import into document with ID from $pq->getDocumentID():
  119 + * pq('<div/>', $pq->getDocumentID())
  120 + * - Import into same document as DOMNode belongs to:
  121 + * pq('<div/>', DOMNode)
  122 + * - Import into document from phpQuery object:
  123 + * pq('<div/>', $pq)
  124 + *
  125 + * 2. Run query:
  126 + * - Run query on last selected document:
  127 + * pq('div.myClass')
  128 + * - Run query on document with ID from $pq->getDocumentID():
  129 + * pq('div.myClass', $pq->getDocumentID())
  130 + * - Run query on same document as DOMNode belongs to and use node(s)as root for query:
  131 + * pq('div.myClass', DOMNode)
  132 + * - Run query on document from phpQuery object
  133 + * and use object's stack as root node(s) for query:
  134 + * pq('div.myClass', $pq)
  135 + *
  136 + * @param string|DOMNode|DOMNodeList|array $arg1 HTML markup, CSS Selector, DOMNode or array of DOMNodes
  137 + * @param string|phpQueryObject|DOMNode $context DOM ID from $pq->getDocumentID(), phpQuery object (determines also query root) or DOMNode (determines also query root)
  138 + *
  139 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery|QueryTemplatesPhpQuery|false
  140 + * phpQuery object or false in case of error.
  141 + */
  142 + public static function pq($arg1, $context = null) {
  143 + if ($arg1 instanceof DOMNODE && ! isset($context)) {
  144 + foreach(phpQuery::$documents as $documentWrapper) {
  145 + $compare = $arg1 instanceof DOMDocument
  146 + ? $arg1 : $arg1->ownerDocument;
  147 + if ($documentWrapper->document->isSameNode($compare))
  148 + $context = $documentWrapper->id;
  149 + }
  150 + }
  151 + if (! $context) {
  152 + $domId = self::$defaultDocumentID;
  153 + if (! $domId)
  154 + throw new Exception("Can't use last created DOM, because there isn't any. Use phpQuery::newDocument() first.");
  155 +// } else if (is_object($context) && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
  156 + } else if (is_object($context) && $context instanceof phpQueryObject)
  157 + $domId = $context->getDocumentID();
  158 + else if ($context instanceof DOMDOCUMENT) {
  159 + $domId = self::getDocumentID($context);
  160 + if (! $domId) {
  161 + //throw new Exception('Orphaned DOMDocument');
  162 + $domId = self::newDocument($context)->getDocumentID();
  163 + }
  164 + } else if ($context instanceof DOMNODE) {
  165 + $domId = self::getDocumentID($context);
  166 + if (! $domId) {
  167 + throw new Exception('Orphaned DOMNode');
  168 +// $domId = self::newDocument($context->ownerDocument);
  169 + }
  170 + } else
  171 + $domId = $context;
  172 + if ($arg1 instanceof phpQueryObject) {
  173 +// if (is_object($arg1) && (get_class($arg1) == 'phpQueryObject' || $arg1 instanceof PHPQUERY || is_subclass_of($arg1, 'phpQueryObject'))) {
  174 + /**
  175 + * Return $arg1 or import $arg1 stack if document differs:
  176 + * pq(pq('<div/>'))
  177 + */
  178 + if ($arg1->getDocumentID() == $domId)
  179 + return $arg1;
  180 + $class = get_class($arg1);
  181 + // support inheritance by passing old object to overloaded constructor
  182 + $phpQuery = $class != 'phpQuery'
  183 + ? new $class($arg1, $domId)
  184 + : new phpQueryObject($domId);
  185 + $phpQuery->elements = array();
  186 + foreach($arg1->elements as $node)
  187 + $phpQuery->elements[] = $phpQuery->document->importNode($node, true);
  188 + return $phpQuery;
  189 + } else if ($arg1 instanceof DOMNODE || (is_array($arg1) && isset($arg1[0]) && $arg1[0] instanceof DOMNODE)) {
  190 + /*
  191 + * Wrap DOM nodes with phpQuery object, import into document when needed:
  192 + * pq(array($domNode1, $domNode2))
  193 + */
  194 + $phpQuery = new phpQueryObject($domId);
  195 + if (!($arg1 instanceof DOMNODELIST) && ! is_array($arg1))
  196 + $arg1 = array($arg1);
  197 + $phpQuery->elements = array();
  198 + foreach($arg1 as $node) {
  199 + $sameDocument = $node->ownerDocument instanceof DOMDOCUMENT
  200 + && ! $node->ownerDocument->isSameNode($phpQuery->document);
  201 + $phpQuery->elements[] = $sameDocument
  202 + ? $phpQuery->document->importNode($node, true)
  203 + : $node;
  204 + }
  205 + return $phpQuery;
  206 + } else if (self::isMarkup($arg1)) {
  207 + /**
  208 + * Import HTML:
  209 + * pq('<div/>')
  210 + */
  211 + $phpQuery = new phpQueryObject($domId);
  212 + return $phpQuery->newInstance(
  213 + $phpQuery->documentWrapper->import($arg1)
  214 + );
  215 + } else {
  216 + /**
  217 + * Run CSS query:
  218 + * pq('div.myClass')
  219 + */
  220 + $phpQuery = new phpQueryObject($domId);
  221 +// if ($context && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
  222 + if ($context && $context instanceof phpQueryObject)
  223 + $phpQuery->elements = $context->elements;
  224 + else if ($context && $context instanceof DOMNODELIST) {
  225 + $phpQuery->elements = array();
  226 + foreach($context as $node)
  227 + $phpQuery->elements[] = $node;
  228 + } else if ($context && $context instanceof DOMNODE)
  229 + $phpQuery->elements = array($context);
  230 + return $phpQuery->find($arg1);
  231 + }
  232 + }
  233 + /**
  234 + * Sets default document to $id. Document has to be loaded prior
  235 + * to using this method.
  236 + * $id can be retrived via getDocumentID() or getDocumentIDRef().
  237 + *
  238 + * @param unknown_type $id
  239 + */
  240 + public static function selectDocument($id) {
  241 + $id = self::getDocumentID($id);
  242 + self::debug("Selecting document '$id' as default one");
  243 + self::$defaultDocumentID = self::getDocumentID($id);
  244 + }
  245 + /**
  246 + * Returns document with id $id or last used as phpQueryObject.
  247 + * $id can be retrived via getDocumentID() or getDocumentIDRef().
  248 + * Chainable.
  249 + *
  250 + * @see phpQuery::selectDocument()
  251 + * @param unknown_type $id
  252 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  253 + */
  254 + public static function getDocument($id = null) {
  255 + if ($id)
  256 + phpQuery::selectDocument($id);
  257 + else
  258 + $id = phpQuery::$defaultDocumentID;
  259 + return new phpQueryObject($id);
  260 + }
  261 + /**
  262 + * Creates new document from markup.
  263 + * Chainable.
  264 + *
  265 + * @param unknown_type $markup
  266 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  267 + */
  268 + public static function newDocument($markup = null, $contentType = null) {
  269 + if (! $markup)
  270 + $markup = '';
  271 + $documentID = phpQuery::createDocumentWrapper($markup, $contentType);
  272 + return new phpQueryObject($documentID);
  273 + }
  274 + /**
  275 + * Creates new document from markup.
  276 + * Chainable.
  277 + *
  278 + * @param unknown_type $markup
  279 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  280 + */
  281 + public static function newDocumentHTML($markup = null, $charset = null) {
  282 + $contentType = $charset
  283 + ? ";charset=$charset"
  284 + : '';
  285 + return self::newDocument($markup, "text/html{$contentType}");
  286 + }
  287 + /**
  288 + * Creates new document from markup.
  289 + * Chainable.
  290 + *
  291 + * @param unknown_type $markup
  292 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  293 + */
  294 + public static function newDocumentXML($markup = null, $charset = null) {
  295 + $contentType = $charset
  296 + ? ";charset=$charset"
  297 + : '';
  298 + return self::newDocument($markup, "text/xml{$contentType}");
  299 + }
  300 + /**
  301 + * Creates new document from markup.
  302 + * Chainable.
  303 + *
  304 + * @param unknown_type $markup
  305 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  306 + */
  307 + public static function newDocumentXHTML($markup = null, $charset = null) {
  308 + $contentType = $charset
  309 + ? ";charset=$charset"
  310 + : '';
  311 + return self::newDocument($markup, "application/xhtml+xml{$contentType}");
  312 + }
  313 + /**
  314 + * Creates new document from markup.
  315 + * Chainable.
  316 + *
  317 + * @param unknown_type $markup
  318 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  319 + */
  320 + public static function newDocumentPHP($markup = null, $contentType = "text/html") {
  321 + // TODO pass charset to phpToMarkup if possible (use DOMDocumentWrapper function)
  322 + $markup = phpQuery::phpToMarkup($markup, self::$defaultCharset);
  323 + return self::newDocument($markup, $contentType);
  324 + }
  325 + public static function phpToMarkup($php, $charset = 'utf-8') {
  326 + $regexes = array(
  327 + '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)<'.'?php?(.*?)(?:\\?>)([^\']*)\'@s',
  328 + '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)<'.'?php?(.*?)(?:\\?>)([^"]*)"@s',
  329 + );
  330 + foreach($regexes as $regex)
  331 + while (preg_match($regex, $php, $matches)) {
  332 + $php = preg_replace_callback(
  333 + $regex,
  334 +// create_function('$m, $charset = "'.$charset.'"',
  335 +// 'return $m[1].$m[2]
  336 +// .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
  337 +// .$m[5].$m[2];'
  338 +// ),
  339 + array('phpQuery', '_phpToMarkupCallback'),
  340 + $php
  341 + );
  342 + }
  343 + $regex = '@(^|>[^<]*)+?(<\?php(.*?)(\?>))@s';
  344 +//preg_match_all($regex, $php, $matches);
  345 +//var_dump($matches);
  346 + $php = preg_replace($regex, '\\1<php><!-- \\3 --></php>', $php);
  347 + return $php;
  348 + }
  349 + public static function _phpToMarkupCallback($php, $charset = 'utf-8') {
  350 + return $m[1].$m[2]
  351 + .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
  352 + .$m[5].$m[2];
  353 + }
  354 + public static function _markupToPHPCallback($m) {
  355 + return "<"."?php ".htmlspecialchars_decode($m[1])." ?".">";
  356 + }
  357 + /**
  358 + * Converts document markup containing PHP code generated by phpQuery::php()
  359 + * into valid (executable) PHP code syntax.
  360 + *
  361 + * @param string|phpQueryObject $content
  362 + * @return string PHP code.
  363 + */
  364 + public static function markupToPHP($content) {
  365 + if ($content instanceof phpQueryObject)
  366 + $content = $content->markupOuter();
  367 + /* <php>...</php> to <?php...? > */
  368 + $content = preg_replace_callback(
  369 + '@<php>\s*<!--(.*?)-->\s*</php>@s',
  370 +// create_function('$m',
  371 +// 'return "<'.'?php ".htmlspecialchars_decode($m[1])." ?'.'>";'
  372 +// ),
  373 + array('phpQuery', '_markupToPHPCallback'),
  374 + $content
  375 + );
  376 + /* <node attr='< ?php ? >'> extra space added to save highlighters */
  377 + $regexes = array(
  378 + '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^\']*)\'@s',
  379 + '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^"]*)"@s',
  380 + );
  381 + foreach($regexes as $regex)
  382 + while (preg_match($regex, $content))
  383 + $content = preg_replace_callback(
  384 + $regex,
  385 + create_function('$m',
  386 + 'return $m[1].$m[2].$m[3]."<?php "
  387 + .str_replace(
  388 + array("%20", "%3E", "%09", "&#10;", "&#9;", "%7B", "%24", "%7D", "%22", "%5B", "%5D"),
  389 + array(" ", ">", " ", "\n", " ", "{", "$", "}", \'"\', "[", "]"),
  390 + htmlspecialchars_decode($m[4])
  391 + )
  392 + ." ?>".$m[5].$m[2];'
  393 + ),
  394 + $content
  395 + );
  396 + return $content;
  397 + }
  398 + /**
  399 + * Creates new document from file $file.
  400 + * Chainable.
  401 + *
  402 + * @param string $file URLs allowed. See File wrapper page at php.net for more supported sources.
  403 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  404 + */
  405 + public static function newDocumentFile($file, $contentType = null) {
  406 + $documentID = self::createDocumentWrapper(
  407 + file_get_contents($file), $contentType
  408 + );
  409 + return new phpQueryObject($documentID);
  410 + }
  411 + /**
  412 + * Creates new document from markup.
  413 + * Chainable.
  414 + *
  415 + * @param unknown_type $markup
  416 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  417 + */
  418 + public static function newDocumentFileHTML($file, $charset = null) {
  419 + $contentType = $charset
  420 + ? ";charset=$charset"
  421 + : '';
  422 + return self::newDocumentFile($file, "text/html{$contentType}");
  423 + }
  424 + /**
  425 + * Creates new document from markup.
  426 + * Chainable.
  427 + *
  428 + * @param unknown_type $markup
  429 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  430 + */
  431 + public static function newDocumentFileXML($file, $charset = null) {
  432 + $contentType = $charset
  433 + ? ";charset=$charset"
  434 + : '';
  435 + return self::newDocumentFile($file, "text/xml{$contentType}");
  436 + }
  437 + /**
  438 + * Creates new document from markup.
  439 + * Chainable.
  440 + *
  441 + * @param unknown_type $markup
  442 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  443 + */
  444 + public static function newDocumentFileXHTML($file, $charset = null) {
  445 + $contentType = $charset
  446 + ? ";charset=$charset"
  447 + : '';
  448 + return self::newDocumentFile($file, "application/xhtml+xml{$contentType}");
  449 + }
  450 + /**
  451 + * Creates new document from markup.
  452 + * Chainable.
  453 + *
  454 + * @param unknown_type $markup
  455 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  456 + */
  457 + public static function newDocumentFilePHP($file, $contentType = null) {
  458 + return self::newDocumentPHP(file_get_contents($file), $contentType);
  459 + }
  460 + /**
  461 + * Reuses existing DOMDocument object.
  462 + * Chainable.
  463 + *
  464 + * @param $document DOMDocument
  465 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  466 + * @TODO support DOMDocument
  467 + */
  468 + public static function loadDocument($document) {
  469 + // TODO
  470 + die('TODO loadDocument');
  471 + }
  472 + /**
  473 + * Enter description here...
  474 + *
  475 + * @param unknown_type $html
  476 + * @param unknown_type $domId
  477 + * @return unknown New DOM ID
  478 + * @todo support PHP tags in input
  479 + * @todo support passing DOMDocument object from self::loadDocument
  480 + */
  481 + protected static function createDocumentWrapper($html, $contentType = null, $documentID = null) {
  482 + if (function_exists('domxml_open_mem'))
  483 + throw new Exception("Old PHP4 DOM XML extension detected. phpQuery won't work until this extension is enabled.");
  484 +// $id = $documentID
  485 +// ? $documentID
  486 +// : md5(microtime());
  487 + $document = null;
  488 + if ($html instanceof DOMDOCUMENT) {
  489 + if (self::getDocumentID($html)) {
  490 + // document already exists in phpQuery::$documents, make a copy
  491 + $document = clone $html;
  492 + } else {
  493 + // new document, add it to phpQuery::$documents
  494 + $wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
  495 + }
  496 + } else {
  497 + $wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
  498 + }
  499 +// $wrapper->id = $id;
  500 + // bind document
  501 + phpQuery::$documents[$wrapper->id] = $wrapper;
  502 + // remember last loaded document
  503 + phpQuery::selectDocument($wrapper->id);
  504 + return $wrapper->id;
  505 + }
  506 + /**
  507 + * Extend class namespace.
  508 + *
  509 + * @param string|array $target
  510 + * @param array $source
  511 + * @TODO support string $source
  512 + * @return unknown_type
  513 + */
  514 + public static function extend($target, $source) {
  515 + switch($target) {
  516 + case 'phpQueryObject':
  517 + $targetRef = &self::$extendMethods;
  518 + $targetRef2 = &self::$pluginsMethods;
  519 + break;
  520 + case 'phpQuery':
  521 + $targetRef = &self::$extendStaticMethods;
  522 + $targetRef2 = &self::$pluginsStaticMethods;
  523 + break;
  524 + default:
  525 + throw new Exception("Unsupported \$target type");
  526 + }
  527 + if (is_string($source))
  528 + $source = array($source => $source);
  529 + foreach($source as $method => $callback) {
  530 + if (isset($targetRef[$method])) {
  531 +// throw new Exception
  532 + self::debug("Duplicate method '{$method}', can\'t extend '{$target}'");
  533 + continue;
  534 + }
  535 + if (isset($targetRef2[$method])) {
  536 +// throw new Exception
  537 + self::debug("Duplicate method '{$method}' from plugin '{$targetRef2[$method]}',"
  538 + ." can\'t extend '{$target}'");
  539 + continue;
  540 + }
  541 + $targetRef[$method] = $callback;
  542 + }
  543 + return true;
  544 + }
  545 + /**
  546 + * Extend phpQuery with $class from $file.
  547 + *
  548 + * @param string $class Extending class name. Real class name can be prepended phpQuery_.
  549 + * @param string $file Filename to include. Defaults to "{$class}.php".
  550 + */
  551 + public static function plugin($class, $file = null) {
  552 + // TODO $class checked agains phpQuery_$class
  553 +// if (strpos($class, 'phpQuery') === 0)
  554 +// $class = substr($class, 8);
  555 + if (in_array($class, self::$pluginsLoaded))
  556 + return true;
  557 + if (! $file)
  558 + $file = $class.'.php';
  559 + $objectClassExists = class_exists('phpQueryObjectPlugin_'.$class);
  560 + $staticClassExists = class_exists('phpQueryPlugin_'.$class);
  561 + if (! $objectClassExists && ! $staticClassExists)
  562 + require_once($file);
  563 + self::$pluginsLoaded[] = $class;
  564 + // static methods
  565 + if (class_exists('phpQueryPlugin_'.$class)) {
  566 + $realClass = 'phpQueryPlugin_'.$class;
  567 + $vars = get_class_vars($realClass);
  568 + $loop = isset($vars['phpQueryMethods'])
  569 + && ! is_null($vars['phpQueryMethods'])
  570 + ? $vars['phpQueryMethods']
  571 + : get_class_methods($realClass);
  572 + foreach($loop as $method) {
  573 + if ($method == '__initialize')
  574 + continue;
  575 + if (! is_callable(array($realClass, $method)))
  576 + continue;
  577 + if (isset(self::$pluginsStaticMethods[$method])) {
  578 + throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsStaticMethods[$method]."'");
  579 + return;
  580 + }
  581 + self::$pluginsStaticMethods[$method] = $class;
  582 + }
  583 + if (method_exists($realClass, '__initialize'))
  584 + call_user_func_array(array($realClass, '__initialize'), array());
  585 + }
  586 + // object methods
  587 + if (class_exists('phpQueryObjectPlugin_'.$class)) {
  588 + $realClass = 'phpQueryObjectPlugin_'.$class;
  589 + $vars = get_class_vars($realClass);
  590 + $loop = isset($vars['phpQueryMethods'])
  591 + && ! is_null($vars['phpQueryMethods'])
  592 + ? $vars['phpQueryMethods']
  593 + : get_class_methods($realClass);
  594 + foreach($loop as $method) {
  595 + if (! is_callable(array($realClass, $method)))
  596 + continue;
  597 + if (isset(self::$pluginsMethods[$method])) {
  598 + throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsMethods[$method]."'");
  599 + continue;
  600 + }
  601 + self::$pluginsMethods[$method] = $class;
  602 + }
  603 + }
  604 + return true;
  605 + }
  606 + /**
  607 + * Unloades all or specified document from memory.
  608 + *
  609 + * @param mixed $documentID @see phpQuery::getDocumentID() for supported types.
  610 + */
  611 + public static function unloadDocuments($id = null) {
  612 + if (isset($id)) {
  613 + if ($id = self::getDocumentID($id))
  614 + unset(phpQuery::$documents[$id]);
  615 + } else {
  616 + foreach(phpQuery::$documents as $k => $v) {
  617 + unset(phpQuery::$documents[$k]);
  618 + }
  619 + }
  620 + }
  621 + /**
  622 + * Parses phpQuery object or HTML result against PHP tags and makes them active.
  623 + *
  624 + * @param phpQuery|string $content
  625 + * @deprecated
  626 + * @return string
  627 + */
  628 + public static function unsafePHPTags($content) {
  629 + return self::markupToPHP($content);
  630 + }
  631 + public static function DOMNodeListToArray($DOMNodeList) {
  632 + $array = array();
  633 + if (! $DOMNodeList)
  634 + return $array;
  635 + foreach($DOMNodeList as $node)
  636 + $array[] = $node;
  637 + return $array;
  638 + }
  639 + /**
  640 + * Checks if $input is HTML string, which has to start with '<'.
  641 + *
  642 + * @deprecated
  643 + * @param String $input
  644 + * @return Bool
  645 + * @todo still used ?
  646 + */
  647 + public static function isMarkup($input) {
  648 + return ! is_array($input) && substr(trim($input), 0, 1) == '<';
  649 + }
  650 + public static function debug($text) {
  651 + if (self::$debug)
  652 + print var_dump($text);
  653 + }
  654 + /**
  655 + * Make an AJAX request.
  656 + *
  657 + * @param array See $options http://docs.jquery.com/Ajax/jQuery.ajax#toptions
  658 + * Additional options are:
  659 + * 'document' - document for global events, @see phpQuery::getDocumentID()
  660 + * 'referer' - implemented
  661 + * 'requested_with' - TODO; not implemented (X-Requested-With)
  662 + * @return Zend_Http_Client
  663 + * @link http://docs.jquery.com/Ajax/jQuery.ajax
  664 + *
  665 + * @TODO $options['cache']
  666 + * @TODO $options['processData']
  667 + * @TODO $options['xhr']
  668 + * @TODO $options['data'] as string
  669 + * @TODO XHR interface
  670 + */
  671 + public static function ajax($options = array(), $xhr = null) {
  672 + $options = array_merge(
  673 + self::$ajaxSettings, $options
  674 + );
  675 + $documentID = isset($options['document'])
  676 + ? self::getDocumentID($options['document'])
  677 + : null;
  678 + if ($xhr) {
  679 + // reuse existing XHR object, but clean it up
  680 + $client = $xhr;
  681 +// $client->setParameterPost(null);
  682 +// $client->setParameterGet(null);
  683 + $client->setAuth(false);
  684 + $client->setHeaders("If-Modified-Since", null);
  685 + $client->setHeaders("Referer", null);
  686 + $client->resetParameters();
  687 + } else {
  688 + // create new XHR object
  689 + require_once('Zend/Http/Client.php');
  690 + $client = new Zend_Http_Client();
  691 + $client->setCookieJar();
  692 + }
  693 + if (isset($options['timeout']))
  694 + $client->setConfig(array(
  695 + 'timeout' => $options['timeout'],
  696 + ));
  697 +// 'maxredirects' => 0,
  698 + foreach(self::$ajaxAllowedHosts as $k => $host)
  699 + if ($host == '.' && isset($_SERVER['HTTP_HOST']))
  700 + self::$ajaxAllowedHosts[$k] = $_SERVER['HTTP_HOST'];
  701 + $host = parse_url($options['url'], PHP_URL_HOST);
  702 + if (! in_array($host, self::$ajaxAllowedHosts)) {
  703 + throw new Exception("Request not permitted, host '$host' not present in "
  704 + ."phpQuery::\$ajaxAllowedHosts");
  705 + }
  706 + // JSONP
  707 + $jsre = "/=\\?(&|$)/";
  708 + if (isset($options['dataType']) && $options['dataType'] == 'jsonp') {
  709 + $jsonpCallbackParam = $options['jsonp']
  710 + ? $options['jsonp'] : 'callback';
  711 + if (strtolower($options['type']) == 'get') {
  712 + if (! preg_match($jsre, $options['url'])) {
  713 + $sep = strpos($options['url'], '?')
  714 + ? '&' : '?';
  715 + $options['url'] .= "$sep$jsonpCallbackParam=?";
  716 + }
  717 + } else if ($options['data']) {
  718 + $jsonp = false;
  719 + foreach($options['data'] as $n => $v) {
  720 + if ($v == '?')
  721 + $jsonp = true;
  722 + }
  723 + if (! $jsonp) {
  724 + $options['data'][$jsonpCallbackParam] = '?';
  725 + }
  726 + }
  727 + $options['dataType'] = 'json';
  728 + }
  729 + if (isset($options['dataType']) && $options['dataType'] == 'json') {
  730 + $jsonpCallback = 'json_'.md5(microtime());
  731 + $jsonpData = $jsonpUrl = false;
  732 + if ($options['data']) {
  733 + foreach($options['data'] as $n => $v) {
  734 + if ($v == '?')
  735 + $jsonpData = $n;
  736 + }
  737 + }
  738 + if (preg_match($jsre, $options['url']))
  739 + $jsonpUrl = true;
  740 + if ($jsonpData !== false || $jsonpUrl) {
  741 + // remember callback name for httpData()
  742 + $options['_jsonp'] = $jsonpCallback;
  743 + if ($jsonpData !== false)
  744 + $options['data'][$jsonpData] = $jsonpCallback;
  745 + if ($jsonpUrl)
  746 + $options['url'] = preg_replace($jsre, "=$jsonpCallback\\1", $options['url']);
  747 + }
  748 + }
  749 + $client->setUri($options['url']);
  750 + $client->setMethod(strtoupper($options['type']));
  751 + if (isset($options['referer']) && $options['referer'])
  752 + $client->setHeaders('Referer', $options['referer']);
  753 + $client->setHeaders(array(
  754 +// 'content-type' => $options['contentType'],
  755 + 'User-Agent' => 'Mozilla/5.0 (X11; U; Linux x86; en-US; rv:1.9.0.5) Gecko'
  756 + .'/2008122010 Firefox/3.0.5',
  757 + // TODO custom charset
  758 + 'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
  759 +// 'Connection' => 'keep-alive',
  760 +// 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  761 + 'Accept-Language' => 'en-us,en;q=0.5',
  762 + ));
  763 + if ($options['username'])
  764 + $client->setAuth($options['username'], $options['password']);
  765 + if (isset($options['ifModified']) && $options['ifModified'])
  766 + $client->setHeaders("If-Modified-Since",
  767 + self::$lastModified
  768 + ? self::$lastModified
  769 + : "Thu, 01 Jan 1970 00:00:00 GMT"
  770 + );
  771 + $client->setHeaders("Accept",
  772 + isset($options['dataType'])
  773 + && isset(self::$ajaxSettings['accepts'][ $options['dataType'] ])
  774 + ? self::$ajaxSettings['accepts'][ $options['dataType'] ].", */*"
  775 + : self::$ajaxSettings['accepts']['_default']
  776 + );
  777 + // TODO $options['processData']
  778 + if ($options['data'] instanceof phpQueryObject) {
  779 + $serialized = $options['data']->serializeArray($options['data']);
  780 + $options['data'] = array();
  781 + foreach($serialized as $r)
  782 + $options['data'][ $r['name'] ] = $r['value'];
  783 + }
  784 + if (strtolower($options['type']) == 'get') {
  785 + $client->setParameterGet($options['data']);
  786 + } else if (strtolower($options['type']) == 'post') {
  787 + $client->setEncType($options['contentType']);
  788 + $client->setParameterPost($options['data']);
  789 + }
  790 + if (self::$active == 0 && $options['global'])
  791 + phpQueryEvents::trigger($documentID, 'ajaxStart');
  792 + self::$active++;
  793 + // beforeSend callback
  794 + if (isset($options['beforeSend']) && $options['beforeSend'])
  795 + phpQuery::callbackRun($options['beforeSend'], array($client));
  796 + // ajaxSend event
  797 + if ($options['global'])
  798 + phpQueryEvents::trigger($documentID, 'ajaxSend', array($client, $options));
  799 + if (phpQuery::$debug) {
  800 + self::debug("{$options['type']}: {$options['url']}\n");
  801 + self::debug("Options: <pre>".var_export($options, true)."</pre>\n");
  802 +// if ($client->getCookieJar())
  803 +// self::debug("Cookies: <pre>".var_export($client->getCookieJar()->getMatchingCookies($options['url']), true)."</pre>\n");
  804 + }
  805 + // request
  806 + $response = $client->request();
  807 + if (phpQuery::$debug) {
  808 + self::debug('Status: '.$response->getStatus().' / '.$response->getMessage());
  809 + self::debug($client->getLastRequest());
  810 + self::debug($response->getHeaders());
  811 + }
  812 + if ($response->isSuccessful()) {
  813 + // XXX tempolary
  814 + self::$lastModified = $response->getHeader('Last-Modified');
  815 + $data = self::httpData($response->getBody(), $options['dataType'], $options);
  816 + if (isset($options['success']) && $options['success'])
  817 + phpQuery::callbackRun($options['success'], array($data, $response->getStatus(), $options));
  818 + if ($options['global'])
  819 + phpQueryEvents::trigger($documentID, 'ajaxSuccess', array($client, $options));
  820 + } else {
  821 + if (isset($options['error']) && $options['error'])
  822 + phpQuery::callbackRun($options['error'], array($client, $response->getStatus(), $response->getMessage()));
  823 + if ($options['global'])
  824 + phpQueryEvents::trigger($documentID, 'ajaxError', array($client, /*$response->getStatus(),*/$response->getMessage(), $options));
  825 + }
  826 + if (isset($options['complete']) && $options['complete'])
  827 + phpQuery::callbackRun($options['complete'], array($client, $response->getStatus()));
  828 + if ($options['global'])
  829 + phpQueryEvents::trigger($documentID, 'ajaxComplete', array($client, $options));
  830 + if ($options['global'] && ! --self::$active)
  831 + phpQueryEvents::trigger($documentID, 'ajaxStop');
  832 + return $client;
  833 +// if (is_null($domId))
  834 +// $domId = self::$defaultDocumentID ? self::$defaultDocumentID : false;
  835 +// return new phpQueryAjaxResponse($response, $domId);
  836 + }
  837 + protected static function httpData($data, $type, $options) {
  838 + if (isset($options['dataFilter']) && $options['dataFilter'])
  839 + $data = self::callbackRun($options['dataFilter'], array($data, $type));
  840 + if (is_string($data)) {
  841 + if ($type == "json") {
  842 + if (isset($options['_jsonp']) && $options['_jsonp']) {
  843 + $data = preg_replace('/^\s*\w+\((.*)\)\s*$/s', '$1', $data);
  844 + }
  845 + $data = self::parseJSON($data);
  846 + }
  847 + }
  848 + return $data;
  849 + }
  850 + /**
  851 + * Enter description here...
  852 + *
  853 + * @param array|phpQuery $data
  854 + *
  855 + */
  856 + public static function param($data) {
  857 + return http_build_query($data, null, '&');
  858 + }
  859 + public static function get($url, $data = null, $callback = null, $type = null) {
  860 + if (!is_array($data)) {
  861 + $callback = $data;
  862 + $data = null;
  863 + }
  864 + // TODO some array_values on this shit
  865 + return phpQuery::ajax(array(
  866 + 'type' => 'GET',
  867 + 'url' => $url,
  868 + 'data' => $data,
  869 + 'success' => $callback,
  870 + 'dataType' => $type,
  871 + ));
  872 + }
  873 + public static function post($url, $data = null, $callback = null, $type = null) {
  874 + if (!is_array($data)) {
  875 + $callback = $data;
  876 + $data = null;
  877 + }
  878 + return phpQuery::ajax(array(
  879 + 'type' => 'POST',
  880 + 'url' => $url,
  881 + 'data' => $data,
  882 + 'success' => $callback,
  883 + 'dataType' => $type,
  884 + ));
  885 + }
  886 + public static function getJSON($url, $data = null, $callback = null) {
  887 + if (!is_array($data)) {
  888 + $callback = $data;
  889 + $data = null;
  890 + }
  891 + // TODO some array_values on this shit
  892 + return phpQuery::ajax(array(
  893 + 'type' => 'GET',
  894 + 'url' => $url,
  895 + 'data' => $data,
  896 + 'success' => $callback,
  897 + 'dataType' => 'json',
  898 + ));
  899 + }
  900 + public static function ajaxSetup($options) {
  901 + self::$ajaxSettings = array_merge(
  902 + self::$ajaxSettings,
  903 + $options
  904 + );
  905 + }
  906 + public static function ajaxAllowHost($host1, $host2 = null, $host3 = null) {
  907 + $loop = is_array($host1)
  908 + ? $host1
  909 + : func_get_args();
  910 + foreach($loop as $host) {
  911 + if ($host && ! in_array($host, phpQuery::$ajaxAllowedHosts)) {
  912 + phpQuery::$ajaxAllowedHosts[] = $host;
  913 + }
  914 + }
  915 + }
  916 + public static function ajaxAllowURL($url1, $url2 = null, $url3 = null) {
  917 + $loop = is_array($url1)
  918 + ? $url1
  919 + : func_get_args();
  920 + foreach($loop as $url)
  921 + phpQuery::ajaxAllowHost(parse_url($url, PHP_URL_HOST));
  922 + }
  923 + /**
  924 + * Returns JSON representation of $data.
  925 + *
  926 + * @static
  927 + * @param mixed $data
  928 + * @return string
  929 + */
  930 + public static function toJSON($data) {
  931 + if (function_exists('json_encode'))
  932 + return json_encode($data);
  933 + require_once('Zend/Json/Encoder.php');
  934 + return Zend_Json_Encoder::encode($data);
  935 + }
  936 + /**
  937 + * Parses JSON into proper PHP type.
  938 + *
  939 + * @static
  940 + * @param string $json
  941 + * @return mixed
  942 + */
  943 + public static function parseJSON($json) {
  944 + if (function_exists('json_decode')) {
  945 + $return = json_decode(trim($json), true);
  946 + // json_decode and UTF8 issues
  947 + if (isset($return))
  948 + return $return;
  949 + }
  950 + require_once('Zend/Json/Decoder.php');
  951 + return Zend_Json_Decoder::decode($json);
  952 + }
  953 + /**
  954 + * Returns source's document ID.
  955 + *
  956 + * @param $source DOMNode|phpQueryObject
  957 + * @return string
  958 + */
  959 + public static function getDocumentID($source) {
  960 + if ($source instanceof DOMDOCUMENT) {
  961 + foreach(phpQuery::$documents as $id => $document) {
  962 + if ($source->isSameNode($document->document))
  963 + return $id;
  964 + }
  965 + } else if ($source instanceof DOMNODE) {
  966 + foreach(phpQuery::$documents as $id => $document) {
  967 + if ($source->ownerDocument->isSameNode($document->document))
  968 + return $id;
  969 + }
  970 + } else if ($source instanceof phpQueryObject)
  971 + return $source->getDocumentID();
  972 + else if (is_string($source) && isset(phpQuery::$documents[$source]))
  973 + return $source;
  974 + }
  975 + /**
  976 + * Get DOMDocument object related to $source.
  977 + * Returns null if such document doesn't exist.
  978 + *
  979 + * @param $source DOMNode|phpQueryObject|string
  980 + * @return string
  981 + */
  982 + public static function getDOMDocument($source) {
  983 + if ($source instanceof DOMDOCUMENT)
  984 + return $source;
  985 + $source = self::getDocumentID($source);
  986 + return $source
  987 + ? self::$documents[$id]['document']
  988 + : null;
  989 + }
  990 +
  991 + // UTILITIES
  992 + // http://docs.jquery.com/Utilities
  993 +
  994 + /**
  995 + *
  996 + * @return unknown_type
  997 + * @link http://docs.jquery.com/Utilities/jQuery.makeArray
  998 + */
  999 + public static function makeArray($obj) {
  1000 + $array = array();
  1001 + if (is_object($object) && $object instanceof DOMNODELIST) {
  1002 + foreach($object as $value)
  1003 + $array[] = $value;
  1004 + } else if (is_object($object) && ! ($object instanceof Iterator)) {
  1005 + foreach(get_object_vars($object) as $name => $value)
  1006 + $array[0][$name] = $value;
  1007 + } else {
  1008 + foreach($object as $name => $value)
  1009 + $array[0][$name] = $value;
  1010 + }
  1011 + return $array;
  1012 + }
  1013 + public static function inArray($value, $array) {
  1014 + return in_array($value, $array);
  1015 + }
  1016 + /**
  1017 + *
  1018 + * @param $object
  1019 + * @param $callback
  1020 + * @return unknown_type
  1021 + * @link http://docs.jquery.com/Utilities/jQuery.each
  1022 + */
  1023 + public static function each($object, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1024 + $paramStructure = null;
  1025 + if (func_num_args() > 2) {
  1026 + $paramStructure = func_get_args();
  1027 + $paramStructure = array_slice($paramStructure, 2);
  1028 + }
  1029 + if (is_object($object) && ! ($object instanceof Iterator)) {
  1030 + foreach(get_object_vars($object) as $name => $value)
  1031 + phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
  1032 + } else {
  1033 + foreach($object as $name => $value)
  1034 + phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
  1035 + }
  1036 + }
  1037 + /**
  1038 + *
  1039 + * @link http://docs.jquery.com/Utilities/jQuery.map
  1040 + */
  1041 + public static function map($array, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1042 + $result = array();
  1043 + $paramStructure = null;
  1044 + if (func_num_args() > 2) {
  1045 + $paramStructure = func_get_args();
  1046 + $paramStructure = array_slice($paramStructure, 2);
  1047 + }
  1048 + foreach($array as $v) {
  1049 + $vv = phpQuery::callbackRun($callback, array($v), $paramStructure);
  1050 +// $callbackArgs = $args;
  1051 +// foreach($args as $i => $arg) {
  1052 +// $callbackArgs[$i] = $arg instanceof CallbackParam
  1053 +// ? $v
  1054 +// : $arg;
  1055 +// }
  1056 +// $vv = call_user_func_array($callback, $callbackArgs);
  1057 + if (is_array($vv)) {
  1058 + foreach($vv as $vvv)
  1059 + $result[] = $vvv;
  1060 + } else if ($vv !== null) {
  1061 + $result[] = $vv;
  1062 + }
  1063 + }
  1064 + return $result;
  1065 + }
  1066 + /**
  1067 + *
  1068 + * @param $callback Callback
  1069 + * @param $params
  1070 + * @param $paramStructure
  1071 + * @return unknown_type
  1072 + */
  1073 + public static function callbackRun($callback, $params = array(), $paramStructure = null) {
  1074 + if (! $callback)
  1075 + return;
  1076 + if ($callback instanceof CallbackParameterToReference) {
  1077 + // TODO support ParamStructure to select which $param push to reference
  1078 + if (isset($params[0]))
  1079 + $callback->callback = $params[0];
  1080 + return true;
  1081 + }
  1082 + if ($callback instanceof Callback) {
  1083 + $paramStructure = $callback->params;
  1084 + $callback = $callback->callback;
  1085 + }
  1086 + if (! $paramStructure)
  1087 + return call_user_func_array($callback, $params);
  1088 + $p = 0;
  1089 + foreach($paramStructure as $i => $v) {
  1090 + $paramStructure[$i] = $v instanceof CallbackParam
  1091 + ? $params[$p++]
  1092 + : $v;
  1093 + }
  1094 + return call_user_func_array($callback, $paramStructure);
  1095 + }
  1096 + /**
  1097 + * Merge 2 phpQuery objects.
  1098 + * @param array $one
  1099 + * @param array $two
  1100 + * @protected
  1101 + * @todo node lists, phpQueryObject
  1102 + */
  1103 + public static function merge($one, $two) {
  1104 + $elements = $one->elements;
  1105 + foreach($two->elements as $node) {
  1106 + $exists = false;
  1107 + foreach($elements as $node2) {
  1108 + if ($node2->isSameNode($node))
  1109 + $exists = true;
  1110 + }
  1111 + if (! $exists)
  1112 + $elements[] = $node;
  1113 + }
  1114 + return $elements;
  1115 +// $one = $one->newInstance();
  1116 +// $one->elements = $elements;
  1117 +// return $one;
  1118 + }
  1119 + /**
  1120 + *
  1121 + * @param $array
  1122 + * @param $callback
  1123 + * @param $invert
  1124 + * @return unknown_type
  1125 + * @link http://docs.jquery.com/Utilities/jQuery.grep
  1126 + */
  1127 + public static function grep($array, $callback, $invert = false) {
  1128 + $result = array();
  1129 + foreach($array as $k => $v) {
  1130 + $r = call_user_func_array($callback, array($v, $k));
  1131 + if ($r === !(bool)$invert)
  1132 + $result[] = $v;
  1133 + }
  1134 + return $result;
  1135 + }
  1136 + public static function unique($array) {
  1137 + return array_unique($array);
  1138 + }
  1139 + /**
  1140 + *
  1141 + * @param $function
  1142 + * @return unknown_type
  1143 + * @TODO there are problems with non-static methods, second parameter pass it
  1144 + * but doesnt verify is method is really callable
  1145 + */
  1146 + public static function isFunction($function) {
  1147 + return is_callable($function);
  1148 + }
  1149 + public static function trim($str) {
  1150 + return trim($str);
  1151 + }
  1152 + /* PLUGINS NAMESPACE */
  1153 + /**
  1154 + *
  1155 + * @param $url
  1156 + * @param $callback
  1157 + * @param $param1
  1158 + * @param $param2
  1159 + * @param $param3
  1160 + * @return phpQueryObject
  1161 + */
  1162 + public static function browserGet($url, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1163 + if (self::plugin('WebBrowser')) {
  1164 + $params = func_get_args();
  1165 + return self::callbackRun(array(self::$plugins, 'browserGet'), $params);
  1166 + } else {
  1167 + self::debug('WebBrowser plugin not available...');
  1168 + }
  1169 + }
  1170 + /**
  1171 + *
  1172 + * @param $url
  1173 + * @param $data
  1174 + * @param $callback
  1175 + * @param $param1
  1176 + * @param $param2
  1177 + * @param $param3
  1178 + * @return phpQueryObject
  1179 + */
  1180 + public static function browserPost($url, $data, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1181 + if (self::plugin('WebBrowser')) {
  1182 + $params = func_get_args();
  1183 + return self::callbackRun(array(self::$plugins, 'browserPost'), $params);
  1184 + } else {
  1185 + self::debug('WebBrowser plugin not available...');
  1186 + }
  1187 + }
  1188 + /**
  1189 + *
  1190 + * @param $ajaxSettings
  1191 + * @param $callback
  1192 + * @param $param1
  1193 + * @param $param2
  1194 + * @param $param3
  1195 + * @return phpQueryObject
  1196 + */
  1197 + public static function browser($ajaxSettings, $callback, $param1 = null, $param2 = null, $param3 = null) {
  1198 + if (self::plugin('WebBrowser')) {
  1199 + $params = func_get_args();
  1200 + return self::callbackRun(array(self::$plugins, 'browser'), $params);
  1201 + } else {
  1202 + self::debug('WebBrowser plugin not available...');
  1203 + }
  1204 + }
  1205 + /**
  1206 + *
  1207 + * @param $code
  1208 + * @return string
  1209 + */
  1210 + public static function php($code) {
  1211 + return self::code('php', $code);
  1212 + }
  1213 + /**
  1214 + *
  1215 + * @param $type
  1216 + * @param $code
  1217 + * @return string
  1218 + */
  1219 + public static function code($type, $code) {
  1220 + return "<$type><!-- ".trim($code)." --></$type>";
  1221 + }
  1222 +
  1223 + public static function __callStatic($method, $params) {
  1224 + return call_user_func_array(
  1225 + array(phpQuery::$plugins, $method),
  1226 + $params
  1227 + );
  1228 + }
  1229 + protected static function dataSetupNode($node, $documentID) {
  1230 + // search are return if alredy exists
  1231 + foreach(phpQuery::$documents[$documentID]->dataNodes as $dataNode) {
  1232 + if ($node->isSameNode($dataNode))
  1233 + return $dataNode;
  1234 + }
  1235 + // if doesn't, add it
  1236 + phpQuery::$documents[$documentID]->dataNodes[] = $node;
  1237 + return $node;
  1238 + }
  1239 + protected static function dataRemoveNode($node, $documentID) {
  1240 + // search are return if alredy exists
  1241 + foreach(phpQuery::$documents[$documentID]->dataNodes as $k => $dataNode) {
  1242 + if ($node->isSameNode($dataNode)) {
  1243 + unset(self::$documents[$documentID]->dataNodes[$k]);
  1244 + unset(self::$documents[$documentID]->data[ $dataNode->dataID ]);
  1245 + }
  1246 + }
  1247 + }
  1248 + public static function data($node, $name, $data, $documentID = null) {
  1249 + if (! $documentID)
  1250 + // TODO check if this works
  1251 + $documentID = self::getDocumentID($node);
  1252 + $document = phpQuery::$documents[$documentID];
  1253 + $node = self::dataSetupNode($node, $documentID);
  1254 + if (! isset($node->dataID))
  1255 + $node->dataID = ++phpQuery::$documents[$documentID]->uuid;
  1256 + $id = $node->dataID;
  1257 + if (! isset($document->data[$id]))
  1258 + $document->data[$id] = array();
  1259 + if (! is_null($data))
  1260 + $document->data[$id][$name] = $data;
  1261 + if ($name) {
  1262 + if (isset($document->data[$id][$name]))
  1263 + return $document->data[$id][$name];
  1264 + } else
  1265 + return $id;
  1266 + }
  1267 + public static function removeData($node, $name, $documentID) {
  1268 + if (! $documentID)
  1269 + // TODO check if this works
  1270 + $documentID = self::getDocumentID($node);
  1271 + $document = phpQuery::$documents[$documentID];
  1272 + $node = self::dataSetupNode($node, $documentID);
  1273 + $id = $node->dataID;
  1274 + if ($name) {
  1275 + if (isset($document->data[$id][$name]))
  1276 + unset($document->data[$id][$name]);
  1277 + $name = null;
  1278 + foreach($document->data[$id] as $name)
  1279 + break;
  1280 + if (! $name)
  1281 + self::removeData($node, $name, $documentID);
  1282 + } else {
  1283 + self::dataRemoveNode($node, $documentID);
  1284 + }
  1285 + }
  1286 +}
  1287 +/**
  1288 + * Plugins static namespace class.
  1289 + *
  1290 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  1291 + * @package phpQuery
  1292 + * @todo move plugin methods here (as statics)
  1293 + */
  1294 +class phpQueryPlugins {
  1295 + public function __call($method, $args) {
  1296 + if (isset(phpQuery::$extendStaticMethods[$method])) {
  1297 + $return = call_user_func_array(
  1298 + phpQuery::$extendStaticMethods[$method],
  1299 + $args
  1300 + );
  1301 + } else if (isset(phpQuery::$pluginsStaticMethods[$method])) {
  1302 + $class = phpQuery::$pluginsStaticMethods[$method];
  1303 + $realClass = "phpQueryPlugin_$class";
  1304 + $return = call_user_func_array(
  1305 + array($realClass, $method),
  1306 + $args
  1307 + );
  1308 + return isset($return)
  1309 + ? $return
  1310 + : $this;
  1311 + } else
  1312 + throw new Exception("Method '{$method}' doesnt exist");
  1313 + }
  1314 +}
  1315 +/**
  1316 + * Shortcut to phpQuery::pq($arg1, $context)
  1317 + * Chainable.
  1318 + *
  1319 + * @see phpQuery::pq()
  1320 + * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
  1321 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  1322 + * @package phpQuery
  1323 + */
  1324 +function pq($arg1, $context = null) {
  1325 + $args = func_get_args();
  1326 + return call_user_func_array(
  1327 + array('phpQuery', 'pq'),
  1328 + $args
  1329 + );
  1330 +}
  1331 +// add plugins dir and Zend framework to include path
  1332 +set_include_path(
  1333 + get_include_path()
  1334 + .PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/'
  1335 + .PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/plugins/'
  1336 +);
  1337 +// why ? no __call nor __get for statics in php...
  1338 +// XXX __callStatic will be available in PHP 5.3
  1339 +phpQuery::$plugins = new phpQueryPlugins();
  1340 +// include bootstrap file (personal library config)
  1341 +if (file_exists(dirname(__FILE__).'/phpQuery/bootstrap.php'))
  1342 + require_once dirname(__FILE__).'/phpQuery/bootstrap.php';
  1 +<?php
  2 +interface ICallbackNamed {
  3 + function hasName();
  4 + function getName();
  5 +}
  6 +/**
  7 + * Callback class introduces currying-like pattern.
  8 + *
  9 + * Example:
  10 + * function foo($param1, $param2, $param3) {
  11 + * var_dump($param1, $param2, $param3);
  12 + * }
  13 + * $fooCurried = new Callback('foo',
  14 + * 'param1 is now statically set',
  15 + * new CallbackParam, new CallbackParam
  16 + * );
  17 + * phpQuery::callbackRun($fooCurried,
  18 + * array('param2 value', 'param3 value'
  19 + * );
  20 + *
  21 + * Callback class is supported in all phpQuery methods which accepts callbacks.
  22 + *
  23 + * @link http://code.google.com/p/phpquery/wiki/Callbacks#Param_Structures
  24 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  25 + *
  26 + * @TODO??? return fake forwarding function created via create_function
  27 + * @TODO honor paramStructure
  28 + */
  29 +class Callback
  30 + implements ICallbackNamed {
  31 + public $callback = null;
  32 + public $params = null;
  33 + protected $name;
  34 + public function __construct($callback, $param1 = null, $param2 = null,
  35 + $param3 = null) {
  36 + $params = func_get_args();
  37 + $params = array_slice($params, 1);
  38 + if ($callback instanceof Callback) {
  39 + // TODO implement recurention
  40 + } else {
  41 + $this->callback = $callback;
  42 + $this->params = $params;
  43 + }
  44 + }
  45 + public function getName() {
  46 + return 'Callback: '.$this->name;
  47 + }
  48 + public function hasName() {
  49 + return isset($this->name) && $this->name;
  50 + }
  51 + public function setName($name) {
  52 + $this->name = $name;
  53 + return $this;
  54 + }
  55 + // TODO test me
  56 +// public function addParams() {
  57 +// $params = func_get_args();
  58 +// return new Callback($this->callback, $this->params+$params);
  59 +// }
  60 +}
  61 +/**
  62 + * Shorthand for new Callback(create_function(...), ...);
  63 + *
  64 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  65 + */
  66 +class CallbackBody extends Callback {
  67 + public function __construct($paramList, $code, $param1 = null, $param2 = null,
  68 + $param3 = null) {
  69 + $params = func_get_args();
  70 + $params = array_slice($params, 2);
  71 + $this->callback = create_function($paramList, $code);
  72 + $this->params = $params;
  73 + }
  74 +}
  75 +/**
  76 + * Callback type which on execution returns reference passed during creation.
  77 + *
  78 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  79 + */
  80 +class CallbackReturnReference extends Callback
  81 + implements ICallbackNamed {
  82 + protected $reference;
  83 + public function __construct(&$reference, $name = null){
  84 + $this->reference =& $reference;
  85 + $this->callback = array($this, 'callback');
  86 + }
  87 + public function callback() {
  88 + return $this->reference;
  89 + }
  90 + public function getName() {
  91 + return 'Callback: '.$this->name;
  92 + }
  93 + public function hasName() {
  94 + return isset($this->name) && $this->name;
  95 + }
  96 +}
  97 +/**
  98 + * Callback type which on execution returns value passed during creation.
  99 + *
  100 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  101 + */
  102 +class CallbackReturnValue extends Callback
  103 + implements ICallbackNamed {
  104 + protected $value;
  105 + protected $name;
  106 + public function __construct($value, $name = null){
  107 + $this->value =& $value;
  108 + $this->name = $name;
  109 + $this->callback = array($this, 'callback');
  110 + }
  111 + public function callback() {
  112 + return $this->value;
  113 + }
  114 + public function __toString() {
  115 + return $this->getName();
  116 + }
  117 + public function getName() {
  118 + return 'Callback: '.$this->name;
  119 + }
  120 + public function hasName() {
  121 + return isset($this->name) && $this->name;
  122 + }
  123 +}
  124 +/**
  125 + * CallbackParameterToReference can be used when we don't really want a callback,
  126 + * only parameter passed to it. CallbackParameterToReference takes first
  127 + * parameter's value and passes it to reference.
  128 + *
  129 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  130 + */
  131 +class CallbackParameterToReference extends Callback {
  132 + /**
  133 + * @param $reference
  134 + * @TODO implement $paramIndex;
  135 + * param index choose which callback param will be passed to reference
  136 + */
  137 + public function __construct(&$reference){
  138 + $this->callback =& $reference;
  139 + }
  140 +}
  141 +//class CallbackReference extends Callback {
  142 +// /**
  143 +// *
  144 +// * @param $reference
  145 +// * @param $paramIndex
  146 +// * @todo implement $paramIndex; param index choose which callback param will be passed to reference
  147 +// */
  148 +// public function __construct(&$reference, $name = null){
  149 +// $this->callback =& $reference;
  150 +// }
  151 +//}
  152 +class CallbackParam {}
  1 +<?php
  2 +/**
  3 + * DOMDocumentWrapper class simplifies work with DOMDocument.
  4 + *
  5 + * Know bug:
  6 + * - in XHTML fragments, <br /> changes to <br clear="none" />
  7 + *
  8 + * @todo check XML catalogs compatibility
  9 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  10 + * @package phpQuery
  11 + */
  12 +class DOMDocumentWrapper {
  13 + /**
  14 + * @var DOMDocument
  15 + */
  16 + public $document;
  17 + public $id;
  18 + /**
  19 + * @todo Rewrite as method and quess if null.
  20 + * @var unknown_type
  21 + */
  22 + public $contentType = '';
  23 + public $xpath;
  24 + public $uuid = 0;
  25 + public $data = array();
  26 + public $dataNodes = array();
  27 + public $events = array();
  28 + public $eventsNodes = array();
  29 + public $eventsGlobal = array();
  30 + /**
  31 + * @TODO iframes support http://code.google.com/p/phpquery/issues/detail?id=28
  32 + * @var unknown_type
  33 + */
  34 + public $frames = array();
  35 + /**
  36 + * Document root, by default equals to document itself.
  37 + * Used by documentFragments.
  38 + *
  39 + * @var DOMNode
  40 + */
  41 + public $root;
  42 + public $isDocumentFragment;
  43 + public $isXML = false;
  44 + public $isXHTML = false;
  45 + public $isHTML = false;
  46 + public $charset;
  47 + public function __construct($markup = null, $contentType = null, $newDocumentID = null) {
  48 + if (isset($markup))
  49 + $this->load($markup, $contentType, $newDocumentID);
  50 + $this->id = $newDocumentID
  51 + ? $newDocumentID
  52 + : md5(microtime());
  53 + }
  54 + public function load($markup, $contentType = null, $newDocumentID = null) {
  55 +// phpQuery::$documents[$id] = $this;
  56 + $this->contentType = strtolower($contentType);
  57 + if ($markup instanceof DOMDOCUMENT) {
  58 + $this->document = $markup;
  59 + $this->root = $this->document;
  60 + $this->charset = $this->document->encoding;
  61 + // TODO isDocumentFragment
  62 + } else {
  63 + $loaded = $this->loadMarkup($markup);
  64 + }
  65 + if ($loaded) {
  66 +// $this->document->formatOutput = true;
  67 + $this->document->preserveWhiteSpace = true;
  68 + $this->xpath = new DOMXPath($this->document);
  69 + $this->afterMarkupLoad();
  70 + return true;
  71 + // remember last loaded document
  72 +// return phpQuery::selectDocument($id);
  73 + }
  74 + return false;
  75 + }
  76 + protected function afterMarkupLoad() {
  77 + if ($this->isXHTML) {
  78 + $this->xpath->registerNamespace("html", "http://www.w3.org/1999/xhtml");
  79 + }
  80 + }
  81 + protected function loadMarkup($markup) {
  82 + $loaded = false;
  83 + if ($this->contentType) {
  84 + self::debug("Load markup for content type {$this->contentType}");
  85 + // content determined by contentType
  86 + list($contentType, $charset) = $this->contentTypeToArray($this->contentType);
  87 + switch($contentType) {
  88 + case 'text/html':
  89 + phpQuery::debug("Loading HTML, content type '{$this->contentType}'");
  90 + $loaded = $this->loadMarkupHTML($markup, $charset);
  91 + break;
  92 + case 'text/xml':
  93 + case 'application/xhtml+xml':
  94 + phpQuery::debug("Loading XML, content type '{$this->contentType}'");
  95 + $loaded = $this->loadMarkupXML($markup, $charset);
  96 + break;
  97 + default:
  98 + // for feeds or anything that sometimes doesn't use text/xml
  99 + if (strpos('xml', $this->contentType) !== false) {
  100 + phpQuery::debug("Loading XML, content type '{$this->contentType}'");
  101 + $loaded = $this->loadMarkupXML($markup, $charset);
  102 + } else
  103 + phpQuery::debug("Could not determine document type from content type '{$this->contentType}'");
  104 + }
  105 + } else {
  106 + // content type autodetection
  107 + if ($this->isXML($markup)) {
  108 + phpQuery::debug("Loading XML, isXML() == true");
  109 + $loaded = $this->loadMarkupXML($markup);
  110 + if (! $loaded && $this->isXHTML) {
  111 + phpQuery::debug('Loading as XML failed, trying to load as HTML, isXHTML == true');
  112 + $loaded = $this->loadMarkupHTML($markup);
  113 + }
  114 + } else {
  115 + phpQuery::debug("Loading HTML, isXML() == false");
  116 + $loaded = $this->loadMarkupHTML($markup);
  117 + }
  118 + }
  119 + return $loaded;
  120 + }
  121 + protected function loadMarkupReset() {
  122 + $this->isXML = $this->isXHTML = $this->isHTML = false;
  123 + }
  124 + protected function documentCreate($charset, $version = '1.0') {
  125 + if (! $version)
  126 + $version = '1.0';
  127 + $this->document = new DOMDocument($version, $charset);
  128 + $this->charset = $this->document->encoding;
  129 +// $this->document->encoding = $charset;
  130 + $this->document->formatOutput = true;
  131 + $this->document->preserveWhiteSpace = true;
  132 + }
  133 + protected function loadMarkupHTML($markup, $requestedCharset = null) {
  134 + if (phpQuery::$debug)
  135 + phpQuery::debug('Full markup load (HTML): '.substr($markup, 0, 250));
  136 + $this->loadMarkupReset();
  137 + $this->isHTML = true;
  138 + if (!isset($this->isDocumentFragment))
  139 + $this->isDocumentFragment = self::isDocumentFragmentHTML($markup);
  140 + $charset = null;
  141 + $documentCharset = $this->charsetFromHTML($markup);
  142 + $addDocumentCharset = false;
  143 + if ($documentCharset) {
  144 + $charset = $documentCharset;
  145 + $markup = $this->charsetFixHTML($markup);
  146 + } else if ($requestedCharset) {
  147 + $charset = $requestedCharset;
  148 + }
  149 + if (! $charset)
  150 + $charset = phpQuery::$defaultCharset;
  151 + // HTTP 1.1 says that the default charset is ISO-8859-1
  152 + // @see http://www.w3.org/International/O-HTTP-charset
  153 + if (! $documentCharset) {
  154 + $documentCharset = 'ISO-8859-1';
  155 + $addDocumentCharset = true;
  156 + }
  157 + // Should be careful here, still need 'magic encoding detection' since lots of pages have other 'default encoding'
  158 + // Worse, some pages can have mixed encodings... we'll try not to worry about that
  159 + $requestedCharset = strtoupper($requestedCharset);
  160 + $documentCharset = strtoupper($documentCharset);
  161 + phpQuery::debug("DOC: $documentCharset REQ: $requestedCharset");
  162 + if ($requestedCharset && $documentCharset && $requestedCharset !== $documentCharset) {
  163 + phpQuery::debug("CHARSET CONVERT");
  164 + // Document Encoding Conversion
  165 + // http://code.google.com/p/phpquery/issues/detail?id=86
  166 + if (function_exists('mb_detect_encoding')) {
  167 + $possibleCharsets = array($documentCharset, $requestedCharset, 'AUTO');
  168 + $docEncoding = mb_detect_encoding($markup, implode(', ', $possibleCharsets));
  169 + if (! $docEncoding)
  170 + $docEncoding = $documentCharset; // ok trust the document
  171 + phpQuery::debug("DETECTED '$docEncoding'");
  172 + // Detected does not match what document says...
  173 + if ($docEncoding !== $documentCharset) {
  174 + // Tricky..
  175 + }
  176 + if ($docEncoding !== $requestedCharset) {
  177 + phpQuery::debug("CONVERT $docEncoding => $requestedCharset");
  178 + $markup = mb_convert_encoding($markup, $requestedCharset, $docEncoding);
  179 + $markup = $this->charsetAppendToHTML($markup, $requestedCharset);
  180 + $charset = $requestedCharset;
  181 + }
  182 + } else {
  183 + phpQuery::debug("TODO: charset conversion without mbstring...");
  184 + }
  185 + }
  186 + $return = false;
  187 + if ($this->isDocumentFragment) {
  188 + phpQuery::debug("Full markup load (HTML), DocumentFragment detected, using charset '$charset'");
  189 + $return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
  190 + } else {
  191 + if ($addDocumentCharset) {
  192 + phpQuery::debug("Full markup load (HTML), appending charset: '$charset'");
  193 + $markup = $this->charsetAppendToHTML($markup, $charset);
  194 + }
  195 + phpQuery::debug("Full markup load (HTML), documentCreate('$charset')");
  196 + $this->documentCreate($charset);
  197 + $return = phpQuery::$debug === 2
  198 + ? $this->document->loadHTML($markup)
  199 + : @$this->document->loadHTML($markup);
  200 + if ($return)
  201 + $this->root = $this->document;
  202 + }
  203 + if ($return && ! $this->contentType)
  204 + $this->contentType = 'text/html';
  205 + return $return;
  206 + }
  207 + protected function loadMarkupXML($markup, $requestedCharset = null) {
  208 + if (phpQuery::$debug)
  209 + phpQuery::debug('Full markup load (XML): '.substr($markup, 0, 250));
  210 + $this->loadMarkupReset();
  211 + $this->isXML = true;
  212 + // check agains XHTML in contentType or markup
  213 + $isContentTypeXHTML = $this->isXHTML();
  214 + $isMarkupXHTML = $this->isXHTML($markup);
  215 + if ($isContentTypeXHTML || $isMarkupXHTML) {
  216 + self::debug('Full markup load (XML), XHTML detected');
  217 + $this->isXHTML = true;
  218 + }
  219 + // determine document fragment
  220 + if (! isset($this->isDocumentFragment))
  221 + $this->isDocumentFragment = $this->isXHTML
  222 + ? self::isDocumentFragmentXHTML($markup)
  223 + : self::isDocumentFragmentXML($markup);
  224 + // this charset will be used
  225 + $charset = null;
  226 + // charset from XML declaration @var string
  227 + $documentCharset = $this->charsetFromXML($markup);
  228 + if (! $documentCharset) {
  229 + if ($this->isXHTML) {
  230 + // this is XHTML, try to get charset from content-type meta header
  231 + $documentCharset = $this->charsetFromHTML($markup);
  232 + if ($documentCharset) {
  233 + phpQuery::debug("Full markup load (XML), appending XHTML charset '$documentCharset'");
  234 + $this->charsetAppendToXML($markup, $documentCharset);
  235 + $charset = $documentCharset;
  236 + }
  237 + }
  238 + if (! $documentCharset) {
  239 + // if still no document charset...
  240 + $charset = $requestedCharset;
  241 + }
  242 + } else if ($requestedCharset) {
  243 + $charset = $requestedCharset;
  244 + }
  245 + if (! $charset) {
  246 + $charset = phpQuery::$defaultCharset;
  247 + }
  248 + if ($requestedCharset && $documentCharset && $requestedCharset != $documentCharset) {
  249 + // TODO place for charset conversion
  250 +// $charset = $requestedCharset;
  251 + }
  252 + $return = false;
  253 + if ($this->isDocumentFragment) {
  254 + phpQuery::debug("Full markup load (XML), DocumentFragment detected, using charset '$charset'");
  255 + $return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
  256 + } else {
  257 + // FIXME ???
  258 + if ($isContentTypeXHTML && ! $isMarkupXHTML)
  259 + if (! $documentCharset) {
  260 + phpQuery::debug("Full markup load (XML), appending charset '$charset'");
  261 + $markup = $this->charsetAppendToXML($markup, $charset);
  262 + }
  263 + // see http://pl2.php.net/manual/en/book.dom.php#78929
  264 + // LIBXML_DTDLOAD (>= PHP 5.1)
  265 + // does XML ctalogues works with LIBXML_NONET
  266 + // $this->document->resolveExternals = true;
  267 + // TODO test LIBXML_COMPACT for performance improvement
  268 + // create document
  269 + $this->documentCreate($charset);
  270 + if (phpversion() < 5.1) {
  271 + $this->document->resolveExternals = true;
  272 + $return = phpQuery::$debug === 2
  273 + ? $this->document->loadXML($markup)
  274 + : @$this->document->loadXML($markup);
  275 + } else {
  276 + /** @link http://pl2.php.net/manual/en/libxml.constants.php */
  277 + $libxmlStatic = phpQuery::$debug === 2
  278 + ? LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET
  279 + : LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOERROR;
  280 + $return = $this->document->loadXML($markup, $libxmlStatic);
  281 +// if (! $return)
  282 +// $return = $this->document->loadHTML($markup);
  283 + }
  284 + if ($return)
  285 + $this->root = $this->document;
  286 + }
  287 + if ($return) {
  288 + if (! $this->contentType) {
  289 + if ($this->isXHTML)
  290 + $this->contentType = 'application/xhtml+xml';
  291 + else
  292 + $this->contentType = 'text/xml';
  293 + }
  294 + return $return;
  295 + } else {
  296 + throw new Exception("Error loading XML markup");
  297 + }
  298 + }
  299 + protected function isXHTML($markup = null) {
  300 + if (! isset($markup)) {
  301 + return strpos($this->contentType, 'xhtml') !== false;
  302 + }
  303 + // XXX ok ?
  304 + return strpos($markup, "<!DOCTYPE html") !== false;
  305 +// return stripos($doctype, 'xhtml') !== false;
  306 +// $doctype = isset($dom->doctype) && is_object($dom->doctype)
  307 +// ? $dom->doctype->publicId
  308 +// : self::$defaultDoctype;
  309 + }
  310 + protected function isXML($markup) {
  311 +// return strpos($markup, '<?xml') !== false && stripos($markup, 'xhtml') === false;
  312 + return strpos(substr($markup, 0, 100), '<'.'?xml') !== false;
  313 + }
  314 + protected function contentTypeToArray($contentType) {
  315 + $matches = explode(';', trim(strtolower($contentType)));
  316 + if (isset($matches[1])) {
  317 + $matches[1] = explode('=', $matches[1]);
  318 + // strip 'charset='
  319 + $matches[1] = isset($matches[1][1]) && trim($matches[1][1])
  320 + ? $matches[1][1]
  321 + : $matches[1][0];
  322 + } else
  323 + $matches[1] = null;
  324 + return $matches;
  325 + }
  326 + /**
  327 + *
  328 + * @param $markup
  329 + * @return array contentType, charset
  330 + */
  331 + protected function contentTypeFromHTML($markup) {
  332 + $matches = array();
  333 + // find meta tag
  334 + preg_match('@<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
  335 + $markup, $matches
  336 + );
  337 + if (! isset($matches[0]))
  338 + return array(null, null);
  339 + // get attr 'content'
  340 + preg_match('@content\\s*=\\s*(["|\'])(.+?)\\1@', $matches[0], $matches);
  341 + if (! isset($matches[0]))
  342 + return array(null, null);
  343 + return $this->contentTypeToArray($matches[2]);
  344 + }
  345 + protected function charsetFromHTML($markup) {
  346 + $contentType = $this->contentTypeFromHTML($markup);
  347 + return $contentType[1];
  348 + }
  349 + protected function charsetFromXML($markup) {
  350 + $matches;
  351 + // find declaration
  352 + preg_match('@<'.'?xml[^>]+encoding\\s*=\\s*(["|\'])(.*?)\\1@i',
  353 + $markup, $matches
  354 + );
  355 + return isset($matches[2])
  356 + ? strtolower($matches[2])
  357 + : null;
  358 + }
  359 + /**
  360 + * Repositions meta[type=charset] at the start of head. Bypasses DOMDocument bug.
  361 + *
  362 + * @link http://code.google.com/p/phpquery/issues/detail?id=80
  363 + * @param $html
  364 + */
  365 + protected function charsetFixHTML($markup) {
  366 + $matches = array();
  367 + // find meta tag
  368 + preg_match('@\s*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
  369 + $markup, $matches, PREG_OFFSET_CAPTURE
  370 + );
  371 + if (! isset($matches[0]))
  372 + return;
  373 + $metaContentType = $matches[0][0];
  374 + $markup = substr($markup, 0, $matches[0][1])
  375 + .substr($markup, $matches[0][1]+strlen($metaContentType));
  376 + $headStart = stripos($markup, '<head>');
  377 + $markup = substr($markup, 0, $headStart+6).$metaContentType
  378 + .substr($markup, $headStart+6);
  379 + return $markup;
  380 + }
  381 + protected function charsetAppendToHTML($html, $charset, $xhtml = false) {
  382 + // remove existing meta[type=content-type]
  383 + $html = preg_replace('@\s*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i', '', $html);
  384 + $meta = '<meta http-equiv="Content-Type" content="text/html;charset='
  385 + .$charset.'" '
  386 + .($xhtml ? '/' : '')
  387 + .'>';
  388 + if (strpos($html, '<head') === false) {
  389 + if (strpos($hltml, '<html') === false) {
  390 + return $meta.$html;
  391 + } else {
  392 + return preg_replace(
  393 + '@<html(.*?)(?(?<!\?)>)@s',
  394 + "<html\\1><head>{$meta}</head>",
  395 + $html
  396 + );
  397 + }
  398 + } else {
  399 + return preg_replace(
  400 + '@<head(.*?)(?(?<!\?)>)@s',
  401 + '<head\\1>'.$meta,
  402 + $html
  403 + );
  404 + }
  405 + }
  406 + protected function charsetAppendToXML($markup, $charset) {
  407 + $declaration = '<'.'?xml version="1.0" encoding="'.$charset.'"?'.'>';
  408 + return $declaration.$markup;
  409 + }
  410 + public static function isDocumentFragmentHTML($markup) {
  411 + return stripos($markup, '<html') === false && stripos($markup, '<!doctype') === false;
  412 + }
  413 + public static function isDocumentFragmentXML($markup) {
  414 + return stripos($markup, '<'.'?xml') === false;
  415 + }
  416 + public static function isDocumentFragmentXHTML($markup) {
  417 + return self::isDocumentFragmentHTML($markup);
  418 + }
  419 + public function importAttr($value) {
  420 + // TODO
  421 + }
  422 + /**
  423 + *
  424 + * @param $source
  425 + * @param $target
  426 + * @param $sourceCharset
  427 + * @return array Array of imported nodes.
  428 + */
  429 + public function import($source, $sourceCharset = null) {
  430 + // TODO charset conversions
  431 + $return = array();
  432 + if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
  433 + $source = array($source);
  434 +// if (is_array($source)) {
  435 +// foreach($source as $node) {
  436 +// if (is_string($node)) {
  437 +// // string markup
  438 +// $fake = $this->documentFragmentCreate($node, $sourceCharset);
  439 +// if ($fake === false)
  440 +// throw new Exception("Error loading documentFragment markup");
  441 +// else
  442 +// $return = array_merge($return,
  443 +// $this->import($fake->root->childNodes)
  444 +// );
  445 +// } else {
  446 +// $return[] = $this->document->importNode($node, true);
  447 +// }
  448 +// }
  449 +// return $return;
  450 +// } else {
  451 +// // string markup
  452 +// $fake = $this->documentFragmentCreate($source, $sourceCharset);
  453 +// if ($fake === false)
  454 +// throw new Exception("Error loading documentFragment markup");
  455 +// else
  456 +// return $this->import($fake->root->childNodes);
  457 +// }
  458 + if (is_array($source) || $source instanceof DOMNODELIST) {
  459 + // dom nodes
  460 + self::debug('Importing nodes to document');
  461 + foreach($source as $node)
  462 + $return[] = $this->document->importNode($node, true);
  463 + } else {
  464 + // string markup
  465 + $fake = $this->documentFragmentCreate($source, $sourceCharset);
  466 + if ($fake === false)
  467 + throw new Exception("Error loading documentFragment markup");
  468 + else
  469 + return $this->import($fake->root->childNodes);
  470 + }
  471 + return $return;
  472 + }
  473 + /**
  474 + * Creates new document fragment.
  475 + *
  476 + * @param $source
  477 + * @return DOMDocumentWrapper
  478 + */
  479 + protected function documentFragmentCreate($source, $charset = null) {
  480 + $fake = new DOMDocumentWrapper();
  481 + $fake->contentType = $this->contentType;
  482 + $fake->isXML = $this->isXML;
  483 + $fake->isHTML = $this->isHTML;
  484 + $fake->isXHTML = $this->isXHTML;
  485 + $fake->root = $fake->document;
  486 + if (! $charset)
  487 + $charset = $this->charset;
  488 +// $fake->documentCreate($this->charset);
  489 + if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
  490 + $source = array($source);
  491 + if (is_array($source) || $source instanceof DOMNODELIST) {
  492 + // dom nodes
  493 + // load fake document
  494 + if (! $this->documentFragmentLoadMarkup($fake, $charset))
  495 + return false;
  496 + $nodes = $fake->import($source);
  497 + foreach($nodes as $node)
  498 + $fake->root->appendChild($node);
  499 + } else {
  500 + // string markup
  501 + $this->documentFragmentLoadMarkup($fake, $charset, $source);
  502 + }
  503 + return $fake;
  504 + }
  505 + /**
  506 + *
  507 + * @param $document DOMDocumentWrapper
  508 + * @param $markup
  509 + * @return $document
  510 + */
  511 + private function documentFragmentLoadMarkup($fragment, $charset, $markup = null) {
  512 + // TODO error handling
  513 + // TODO copy doctype
  514 + // tempolary turn off
  515 + $fragment->isDocumentFragment = false;
  516 + if ($fragment->isXML) {
  517 + if ($fragment->isXHTML) {
  518 + // add FAKE element to set default namespace
  519 + $fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?>'
  520 + .'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
  521 + .'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
  522 + .'<fake xmlns="http://www.w3.org/1999/xhtml">'.$markup.'</fake>');
  523 + $fragment->root = $fragment->document->firstChild->nextSibling;
  524 + } else {
  525 + $fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?><fake>'.$markup.'</fake>');
  526 + $fragment->root = $fragment->document->firstChild;
  527 + }
  528 + } else {
  529 + $markup2 = phpQuery::$defaultDoctype.'<html><head><meta http-equiv="Content-Type" content="text/html;charset='
  530 + .$charset.'"></head>';
  531 + $noBody = strpos($markup, '<body') === false;
  532 + if ($noBody)
  533 + $markup2 .= '<body>';
  534 + $markup2 .= $markup;
  535 + if ($noBody)
  536 + $markup2 .= '</body>';
  537 + $markup2 .= '</html>';
  538 + $fragment->loadMarkupHTML($markup2);
  539 + // TODO resolv body tag merging issue
  540 + $fragment->root = $noBody
  541 + ? $fragment->document->firstChild->nextSibling->firstChild->nextSibling
  542 + : $fragment->document->firstChild->nextSibling->firstChild->nextSibling;
  543 + }
  544 + if (! $fragment->root)
  545 + return false;
  546 + $fragment->isDocumentFragment = true;
  547 + return true;
  548 + }
  549 + protected function documentFragmentToMarkup($fragment) {
  550 + phpQuery::debug('documentFragmentToMarkup');
  551 + $tmp = $fragment->isDocumentFragment;
  552 + $fragment->isDocumentFragment = false;
  553 + $markup = $fragment->markup();
  554 + if ($fragment->isXML) {
  555 + $markup = substr($markup, 0, strrpos($markup, '</fake>'));
  556 + if ($fragment->isXHTML) {
  557 + $markup = substr($markup, strpos($markup, '<fake')+43);
  558 + } else {
  559 + $markup = substr($markup, strpos($markup, '<fake>')+6);
  560 + }
  561 + } else {
  562 + $markup = substr($markup, strpos($markup, '<body>')+6);
  563 + $markup = substr($markup, 0, strrpos($markup, '</body>'));
  564 + }
  565 + $fragment->isDocumentFragment = $tmp;
  566 + if (phpQuery::$debug)
  567 + phpQuery::debug('documentFragmentToMarkup: '.substr($markup, 0, 150));
  568 + return $markup;
  569 + }
  570 + /**
  571 + * Return document markup, starting with optional $nodes as root.
  572 + *
  573 + * @param $nodes DOMNode|DOMNodeList
  574 + * @return string
  575 + */
  576 + public function markup($nodes = null, $innerMarkup = false) {
  577 + if (isset($nodes) && count($nodes) == 1 && $nodes[0] instanceof DOMDOCUMENT)
  578 + $nodes = null;
  579 + if (isset($nodes)) {
  580 + $markup = '';
  581 + if (!is_array($nodes) && !($nodes instanceof DOMNODELIST) )
  582 + $nodes = array($nodes);
  583 + if ($this->isDocumentFragment && ! $innerMarkup)
  584 + foreach($nodes as $i => $node)
  585 + if ($node->isSameNode($this->root)) {
  586 + // var_dump($node);
  587 + $nodes = array_slice($nodes, 0, $i)
  588 + + phpQuery::DOMNodeListToArray($node->childNodes)
  589 + + array_slice($nodes, $i+1);
  590 + }
  591 + if ($this->isXML && ! $innerMarkup) {
  592 + self::debug("Getting outerXML with charset '{$this->charset}'");
  593 + // we need outerXML, so we can benefit from
  594 + // $node param support in saveXML()
  595 + foreach($nodes as $node)
  596 + $markup .= $this->document->saveXML($node);
  597 + } else {
  598 + $loop = array();
  599 + if ($innerMarkup)
  600 + foreach($nodes as $node) {
  601 + if ($node->childNodes)
  602 + foreach($node->childNodes as $child)
  603 + $loop[] = $child;
  604 + else
  605 + $loop[] = $node;
  606 + }
  607 + else
  608 + $loop = $nodes;
  609 + self::debug("Getting markup, moving selected nodes (".count($loop).") to new DocumentFragment");
  610 + $fake = $this->documentFragmentCreate($loop);
  611 + $markup = $this->documentFragmentToMarkup($fake);
  612 + }
  613 + if ($this->isXHTML) {
  614 + self::debug("Fixing XHTML");
  615 + $markup = self::markupFixXHTML($markup);
  616 + }
  617 + self::debug("Markup: ".substr($markup, 0, 250));
  618 + return $markup;
  619 + } else {
  620 + if ($this->isDocumentFragment) {
  621 + // documentFragment, html only...
  622 + self::debug("Getting markup, DocumentFragment detected");
  623 +// return $this->markup(
  624 +//// $this->document->getElementsByTagName('body')->item(0)
  625 +// $this->document->root, true
  626 +// );
  627 + $markup = $this->documentFragmentToMarkup($this);
  628 + // no need for markupFixXHTML, as it's done thought markup($nodes) method
  629 + return $markup;
  630 + } else {
  631 + self::debug("Getting markup (".($this->isXML?'XML':'HTML')."), final with charset '{$this->charset}'");
  632 + $markup = $this->isXML
  633 + ? $this->document->saveXML()
  634 + : $this->document->saveHTML();
  635 + if ($this->isXHTML) {
  636 + self::debug("Fixing XHTML");
  637 + $markup = self::markupFixXHTML($markup);
  638 + }
  639 + self::debug("Markup: ".substr($markup, 0, 250));
  640 + return $markup;
  641 + }
  642 + }
  643 + }
  644 + protected static function markupFixXHTML($markup) {
  645 + $markup = self::expandEmptyTag('script', $markup);
  646 + $markup = self::expandEmptyTag('select', $markup);
  647 + $markup = self::expandEmptyTag('textarea', $markup);
  648 + return $markup;
  649 + }
  650 + public static function debug($text) {
  651 + phpQuery::debug($text);
  652 + }
  653 + /**
  654 + * expandEmptyTag
  655 + *
  656 + * @param $tag
  657 + * @param $xml
  658 + * @return unknown_type
  659 + * @author mjaque at ilkebenson dot com
  660 + * @link http://php.net/manual/en/domdocument.savehtml.php#81256
  661 + */
  662 + public static function expandEmptyTag($tag, $xml){
  663 + $indice = 0;
  664 + while ($indice< strlen($xml)){
  665 + $pos = strpos($xml, "<$tag ", $indice);
  666 + if ($pos){
  667 + $posCierre = strpos($xml, ">", $pos);
  668 + if ($xml[$posCierre-1] == "/"){
  669 + $xml = substr_replace($xml, "></$tag>", $posCierre-1, 2);
  670 + }
  671 + $indice = $posCierre;
  672 + }
  673 + else break;
  674 + }
  675 + return $xml;
  676 + }
  677 +}
  1 +<?php
  2 +/**
  3 + * DOMEvent class.
  4 + *
  5 + * Based on
  6 + * @link http://developer.mozilla.org/En/DOM:event
  7 + * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
  8 + * @package phpQuery
  9 + * @todo implement ArrayAccess ?
  10 + */
  11 +class DOMEvent {
  12 + /**
  13 + * Returns a boolean indicating whether the event bubbles up through the DOM or not.
  14 + *
  15 + * @var unknown_type
  16 + */
  17 + public $bubbles = true;
  18 + /**
  19 + * Returns a boolean indicating whether the event is cancelable.
  20 + *
  21 + * @var unknown_type
  22 + */
  23 + public $cancelable = true;
  24 + /**
  25 + * Returns a reference to the currently registered target for the event.
  26 + *
  27 + * @var unknown_type
  28 + */
  29 + public $currentTarget;
  30 + /**
  31 + * Returns detail about the event, depending on the type of event.
  32 + *
  33 + * @var unknown_type
  34 + * @link http://developer.mozilla.org/en/DOM/event.detail
  35 + */
  36 + public $detail; // ???
  37 + /**
  38 + * Used to indicate which phase of the event flow is currently being evaluated.
  39 + *
  40 + * NOT IMPLEMENTED
  41 + *
  42 + * @var unknown_type
  43 + * @link http://developer.mozilla.org/en/DOM/event.eventPhase
  44 + */
  45 + public $eventPhase; // ???
  46 + /**
  47 + * The explicit original target of the event (Mozilla-specific).
  48 + *
  49 + * NOT IMPLEMENTED
  50 + *
  51 + * @var unknown_type
  52 + */
  53 + public $explicitOriginalTarget; // moz only
  54 + /**
  55 + * The original target of the event, before any retargetings (Mozilla-specific).
  56 + *
  57 + * NOT IMPLEMENTED
  58 + *
  59 + * @var unknown_type
  60 + */
  61 + public $originalTarget; // moz only
  62 + /**
  63 + * Identifies a secondary target for the event.
  64 + *
  65 + * @var unknown_type
  66 + */
  67 + public $relatedTarget;
  68 + /**
  69 + * Returns a reference to the target to which the event was originally dispatched.
  70 + *
  71 + * @var unknown_type
  72 + */
  73 + public $target;
  74 + /**
  75 + * Returns the time that the event was created.
  76 + *
  77 + * @var unknown_type
  78 + */
  79 + public $timeStamp;
  80 + /**
  81 + * Returns the name of the event (case-insensitive).
  82 + */
  83 + public $type;
  84 + public $runDefault = true;
  85 + public $data = null;
  86 + public function __construct($data) {
  87 + foreach($data as $k => $v) {
  88 + $this->$k = $v;
  89 + }
  90 + if (! $this->timeStamp)
  91 + $this->timeStamp = time();
  92 + }
  93 + /**
  94 + * Cancels the event (if it is cancelable).
  95 + *
  96 + */
  97 + public function preventDefault() {
  98 + $this->runDefault = false;
  99 + }
  100 + /**
  101 + * Stops the propagation of events further along in the DOM.
  102 + *
  103 + */
  104 + public function stopPropagation() {
  105 + $this->bubbles = false;
  106 + }
  107 +}
  1 +<?php
  2 +/**
  3 + * Zend Framework
  4 + *
  5 + * LICENSE
  6 + *
  7 + * This source file is subject to the new BSD license that is bundled
  8 + * with this package in the file LICENSE.txt.
  9 + * It is also available through the world-wide-web at this URL:
  10 + * http://framework.zend.com/license/new-bsd
  11 + * If you did not receive a copy of the license and are unable to
  12 + * obtain it through the world-wide-web, please send an email
  13 + * to license@zend.com so we can send you a copy immediately.
  14 + *
  15 + * @category Zend
  16 + * @package Zend
  17 + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  18 + * @license http://framework.zend.com/license/new-bsd New BSD License
  19 + */
  20 +
  21 +
  22 +/**
  23 + * @category Zend
  24 + * @package Zend
  25 + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  26 + * @license http://framework.zend.com/license/new-bsd New BSD License
  27 + */
  28 +class Zend_Exception extends Exception
  29 +{}
  30 +