Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a9a83fb47 | ||
|
|
593338f8f8 | ||
|
|
f403e94463 | ||
|
|
ec467a6f33 | ||
|
|
4b7289618f | ||
|
|
b716689318 | ||
|
|
b99af56576 | ||
|
|
45219c7b39 | ||
|
|
98a6ced6b0 | ||
|
|
620dc8bc72 | ||
|
|
3ad0d09569 | ||
|
|
9fdd698e09 | ||
|
|
ea6254fc80 | ||
|
|
566be5ce9f | ||
|
|
959aab1bc7 | ||
|
|
3f0534e0e1 | ||
|
|
6d0ca768f9 | ||
|
|
1bf6169ffc | ||
|
|
6959b13189 | ||
|
|
2631fda37b | ||
|
|
3fcd32d767 | ||
|
|
b371252f27 | ||
|
|
18992b2d5c | ||
|
|
5cf5f6b2ae | ||
|
|
45d2c3f117 | ||
|
|
f58e47d630 | ||
|
|
b23f970bea |
1
CHANGELOG.md
Normal file
1
CHANGELOG.md
Normal file
@@ -0,0 +1 @@
|
||||
https://github.com/leokhoa/laragon/releases
|
||||
@@ -6,12 +6,14 @@ Laragon is a portable, isolated, fast & powerful universal development environme
|
||||
|
||||
Laragon is great for building and managing modern web applications. It is focused on performance - designed around stability, simplicity, flexibility and freedom.
|
||||
|
||||
Laragon is very lightweight and will stay as lean as possible. The core binary itself is less than 2MB and uses less than 4MB RAM when running.
|
||||
Laragon is very lightweight and will stay as lean as possible. The core binary itself is less than 6MB and uses around 4MB - 10MB RAM when running.
|
||||
|
||||
Laragon doesn't use Windows services. It has its own `service orchestration` which manages services asynchronously and non-blocking so you'll find things run fast & smoothly with Laragon.
|
||||
|
||||
Enjoy!
|
||||
|
||||
> Starting with Laragon 7.x, a license is required to use Laragon. For more details, visit <https://laragon.lemonsqueezy.com/>.
|
||||
|
||||
## Features
|
||||
|
||||
- **Pretty URLs**
|
||||
|
||||
13
SECURITY.md
Normal file
13
SECURITY.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |Reason |
|
||||
| ------- | ------------------ | ---------- |
|
||||
| >=7.0 | :white_check_mark: | |
|
||||
| <=6.0 | :x: | EOL |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a security vulnerability in Laragon, please report it via email to Leo Khoa at leo@laragon.org
|
||||
We take security seriously, and all reports will be promptly addressed.
|
||||
@@ -13,5 +13,3 @@ SSLProxyProtocol all -SSLv3
|
||||
|
||||
SSLSessionCache "shmcb:logs/ssl_scache(512000)"
|
||||
SSLSessionCacheTimeout 300
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,198 +0,0 @@
|
||||
Change Log
|
||||
============================
|
||||
### Version 2.0 ###
|
||||
|
||||
SimpleAjaxUploader.js:
|
||||
|
||||
* Added support for drag and drop file uploads
|
||||
* Added `ss.uploadSetup()` method to set default uploader option values (useful for multiple uploader instances)
|
||||
* Added `noParams` option to disable the default behavior of appending the file name to the URL query string
|
||||
* Numerous code improvements throughout -- bug fixes, memory usage, etc.
|
||||
|
||||
Uploader.php
|
||||
* Refactored into a single class in accordance with one class, one file
|
||||
* Made improvements to error detection and handling
|
||||
* Added support for reading the `X-File-Name` header as an alternative to query string parameters for sending file names to the server
|
||||
* Set default value of the `$uploadName` property to be `"uploadfile"` for consistency with the examples - <a href="https://github.com/LPology/Simple-Ajax-Uploader/issues/72">72</a>
|
||||
|
||||
### Version 1.11 ###
|
||||
* Added support for PHP Session Upload Progress for PHP 5.4+ (APC was deprecated in 5.3)
|
||||
* Added `clearQueue()` method which gives the user the ability to clear all files in queue - <a href="https://github.com/LPology/Simple-Ajax-Uploader/pull/62">#62</a> - (Special thanks to <a href="https://github.com/mouse0270">mouse0270</a> for this one)
|
||||
* Fixed multiple file selection bug - <a href="https://github.com/LPology/Simple-Ajax-Uploader/pull/67">#67</a> - (Special thanks to <a href="https://github.com/genintho">genintho</a> for this)
|
||||
* Fixed bug which could allow form/input elements to be created with invalid name/ID attributes in IE7-9
|
||||
|
||||
### Version 1.10.1 ###
|
||||
* `iframe` and `form` elements are now created with `document.createElement()` rather than the much slower HTML injection method
|
||||
* Removed unused variable from `_uploadIframe()`
|
||||
|
||||
### Version 1.10 ###
|
||||
* Added `setOptions()` method for setting or changing upload options - <a href="https://github.com/LPology/Simple-Ajax-Uploader/issues/54">#54</a> - (special thanks to <a href="https://github.com/hauru">hauru</a> for this)
|
||||
* Added `customHeader` option for sending custom request headers - <a href="https://github.com/LPology/Simple-Ajax-Uploader/issues/47">#47</a> (special thanks to <a href="https://github.com/cillosis">cillosis</a> for this)
|
||||
* Updated `ss.parseJSON()` to use a more secure method of manually parsing JSON
|
||||
|
||||
### Version 1.9.1 ###
|
||||
* `onError()` callback now receives server response as an argument, if it exists, or `false` if it does not - <a href="https://github.com/LPology/Simple-Ajax-Uploader/pull/37">#37</a> (special thanks to <a href="https://github.com/KSDaemon">KSDaemon</a> for this)
|
||||
<br />
|
||||
<br />
|
||||
<strong>API Change Note:</strong> For consistency with the other callbacks, the server response is passed to `onError()` as the next to last argument, directly before the upload button. Therefore, if you use the upload button parameter in `onError()`, you will need to update your code when upgrading.
|
||||
<br />
|
||||
<br />
|
||||
* Switched from Google Closure Compiler to YUI Compressor for minification
|
||||
|
||||
### Version 1.9 ###
|
||||
* Added CORS support - <a href="http://www.lpology.com/code/ajaxuploader/How-to-Cross-Domain-File-Uploading.php">Learn more</a>
|
||||
* Query string parameters for Nginx Upload Progress Module in `_uploadIframe()` are now encoded with `encodeURIComponent()`
|
||||
* Upload progress ID keys are now generated prior to each upload instead of on page load
|
||||
* Query string parameters passed to `url` are now preserved - <a href="https://github.com/LPology/Simple-Ajax-Uploader/issues/34">#34</a> (special thanks to <a href="https://github.com/Deefjuh">Deefjuh</a> for this)
|
||||
|
||||
### Version 1.8.2 ###
|
||||
* A reference to the button which triggers an upload is now passed as the last argument to the following callbacks: `onAbort()`, `onChange()`, `onSubmit()`, `onComplete()`, `onError()`, `startXHR()`, `endXHR()`, `startNonXHR()`, `endNonXHR()` (can be useful when using multiple upload buttons)
|
||||
* Fixed bug which caused some methods to not work if called inside of `startXHR()` or `startNonXHR()`
|
||||
* Fixed bug causing undefined variable in IE9 and older if `progressUrl` and `nginxProgressUrl` are not set
|
||||
|
||||
### Version 1.8.1 ###
|
||||
* Added `destroy()` method for completely removing upload functionality
|
||||
* Removed redundant call to `ss.verifyElem()` inside of `rerouteClicks()`
|
||||
* Moved browser-specific checks to top of IIFE, as they only need to execute once
|
||||
|
||||
### Version 1.8 ###
|
||||
SimpleAjaxUploader.js:
|
||||
* Added support for <a href="http://wiki.nginx.org/HttpUploadProgressModule">Nginx Upload Progress Module</a>
|
||||
* Added `setAbortBtn()` method to designate an element as "cancel" button
|
||||
* Added `onAbort()` callback function to specify behavior upon manual abort
|
||||
* Added `setPctBox()` method to designate an element to be injected with upload progress percentage
|
||||
* Switched to a unique ID function that is RFC 4122 version 4 compliant
|
||||
* The `button` option now accepts either a single button (element ID string, element, or jQuery object), or an array of buttons. If an array is passed, each element in the array will work as an upload button
|
||||
* Upload progress update request keys are now locally generated
|
||||
* Fixed bug that was causing `onError()` to be fired twice
|
||||
* For consistency with jQuery behavior, any 2xx status code is now handled as a successful response (previously, only `200` and `201` were successful)
|
||||
* Upload buttons are now being properly disabled/enabled at correct points
|
||||
* Made significant improvements to error handling, particularly with iframe uploads and retrieving server provided progress updates
|
||||
* Fixed a number of potential memory leaks for Internet Explorer
|
||||
* Regular expressions are now pre-compiled and cached for better performance
|
||||
* For server progress tracking, `sizeBox` and `onUpdateFileSize()` are no longer pointlessly set/called again after first progress update is received
|
||||
|
||||
uploadProgress.php:
|
||||
* Removed functionality for returning upload keys, as RFC 4122 v4 compliant UUIDs are now generated client side
|
||||
|
||||
### Version 1.7 ###
|
||||
SimpleAjaxUploader.js:
|
||||
* Fixed IE6/IE7 memory leak when removing elements without first removing event listeners (<a href="https://github.com/LPology/Simple-Ajax-Uploader/issues/21">issue #21</a>)
|
||||
* Fixed possible race condition in which `removeCurrent()` could potentially delete the wrong file from the upload queue
|
||||
* Multiple file inputs are now disabled in Safari due to a browser bug that just screws everything up (see: http://stackoverflow.com/q/7231054/1091949)
|
||||
* Switched to a smaller, faster process for cross-browser bounding box calculation
|
||||
* Updated to faster methods of checking for, adding, and removing element CSS classes
|
||||
* Combined `_checkExtension()` with `_checkFile()` to eliminate a function call/reduce code size
|
||||
* Combined `_handleIframeResponse()` with `_uploadIframe()` and switched to a more efficient method of getting iframe contents
|
||||
* Removed a number of unnecessary/redundant function calls, along with some unnecessary variable copying
|
||||
* Updated `ss.verifyElem()` to use the much faster `charAt()` and `substr()` in place of a regex and `slice()`
|
||||
* Added separate feature detection for file input `accept` attribute
|
||||
|
||||
Uploader.php:
|
||||
* Removed unnecessary check of `$allowedExtensions` for `null` value in `handleUpload()`
|
||||
* Added `final` keyword to `FileUploadXHR` and `FileUploadPOSTForm` classes and their respective methods to discourage direct use
|
||||
|
||||
### Version 1.6.5 ###
|
||||
* When using `multipart`, additional data will now also appended to the multipart form.
|
||||
* Cleaned up some messy code -- organization, unnecessary variable copying, etc.
|
||||
|
||||
### Version 1.6.4 ###
|
||||
* Switched from using `setAttribute` to dot notation for setting element properties (some versions of IE don't handle `setAttribute` well)
|
||||
* `ss.removeItem()` now uses the faster countdown method to loop through arrays
|
||||
* In accordance with W3 standards, `_uploadXhr()` now accepts either a `200 OK` or `201 Created` as a successful response
|
||||
* Uploader.php -- the `handleUpload()` method now checks whether the `allowedExtensions` property is `empty` instead of `null`. This prevents an "Invalid file type" error resulting from passing an empty array
|
||||
|
||||
### Version 1.6.3 ###
|
||||
* Fixed bug which allowed `onComplete()` to be called after JSON parse error
|
||||
|
||||
### Version 1.6.2 ###
|
||||
* Overhauled error handling to fix a number of issues.
|
||||
* Added consistent error types for `onError()` so that the second parameter will be either:
|
||||
`parseerror` (bad JSON from server), `transfererror` (xfer error during XHR upload), `servererror` (server response not `200 OK`)
|
||||
* Fixed problem with null file size parameter for `endXHR()` callback
|
||||
|
||||
### Version 1.6.1 ###
|
||||
* Plugin is now wrapped in an IIFE
|
||||
* Leading semicolon added to close any previous statement
|
||||
* Code is now in strict mode
|
||||
* Cleaned up a few messy areas
|
||||
|
||||
### Version 1.6 ###
|
||||
If the 1.6 release has a theme, it is flexibility. Nearly every update in this release is intended to allow greater flexibility for developers.
|
||||
|
||||
* Submitting a file which exceeds `maxSize` or is not an `allowedExtension` no longer triggers an alert, but will instead fire a callback
|
||||
* Added `onSizeError()` callback function which fires when a file exceeds the `maxSize` option, if it is set
|
||||
* Added `onExtError()` callback which fires when a file is not permitted by the `allowedExtensions` option, if it is set
|
||||
* Removed `messages` option and `_errorMsg()`, both of which are no longer used
|
||||
* Added new `accept` option, the value of which will be the value of the `accept` file input attribute in supporting browsers. <a href="http://stackoverflow.com/a/10503561/1091949">More info.</a>
|
||||
* Added new `method` option to allow specifying an HTTP method other than POST
|
||||
|
||||
Special thanks to <a href="https://github.com/dleffler">dleffler</a>, <a href="https://github.com/devtrends">devtrends</a> and <a href="https://github.com/urcadox">urcadox</a> for their ideas and feedback.
|
||||
|
||||
### Version 1.5.3 ###
|
||||
* Added `autoSubmit` check before submitting in `_cycleQueue()`
|
||||
* Added check to ensure upload progress server key doesn't exceed 57 characters (max allowable APC key length)
|
||||
* `rerouteClicks(element)` can now be used to add additional elements which can be clicked to open file box
|
||||
|
||||
### Version 1.5.2 ###
|
||||
(This isn't as much a release as it is a signal to update for anyone who may have downloaded version 1.5.1 in the past few hours)
|
||||
* Fixed "bug" from 1.5.1 that broke uploader without multiple option enabled
|
||||
* Added `queue` option to disable automatic file queuing
|
||||
|
||||
### Version 1.5.1 ###
|
||||
* Multiple file inputs are now used in browsers with support for File API, thus allowing multiple file selection if `multiple` option is `true`
|
||||
* Removed some unnecessary variable copying
|
||||
* Added queue system which allows files to be selected and automatically uploaded as others finish
|
||||
* Added `getQueueSize()` function to get current number of files waiting in queue
|
||||
* Fixed bug in which active upload counter was not properly updating when returning `false` from `startXHR()` and `startNonXHR()`
|
||||
* Error messages now incorporate file names
|
||||
|
||||
### Version 1.5 ###
|
||||
* Added support for multiple file uploading, along with Gmail-style multiple progress bars for tracking each file.
|
||||
* Added new `maxSize` option for file size limits, `allowedExtensions` option for file type restrictions. Custom error messages supported for both.
|
||||
* Updated `verifyElem()` to use a better method of detecting if an upload button is a jQuery object.
|
||||
* Numerous code enhancements throughout - updated error handling, cleaner organization, performance improvements.
|
||||
* Patched some memory leaks created by circular references in event handlers.
|
||||
|
||||
### Version 1.4.2 ###
|
||||
* SimpleAjaxUploader.js - Added `multipart` option to allow multipart form upload instead of binary stream
|
||||
* Uploader.php - The check for form uploads is now first in the constructor to accomodate new `multipart` option
|
||||
* Uploader.php - Providing an array of valid file extensions is now optional. If not provided, all file types are allowed
|
||||
* Added minified version of JS file
|
||||
|
||||
### Version 1.4.1 ###
|
||||
* Fixed XHR status check logic that could allow false alarm calls to onError callback
|
||||
* Removed redundant XHR status check
|
||||
* Returning false from a callback no longer clears the file field. Not sure why it ever did to begin with.
|
||||
* A status check now occurs prior to progress update requests to prevent potential loop that could be caused by a server error
|
||||
* Parsing JSON in older browsers no longer uses `eval` because it's evil
|
||||
|
||||
### Version 1.4 ###
|
||||
This release includes a major overhaul that adds functionality for implementing cross-browser upload progress support. Through feature detection and abstraction, it is now possible for the `onProgress` callback function to maintain consistent behavior across browsers.
|
||||
|
||||
Currently, only PHP (with APC extension) is supported. To use, set the newly added `progressUrl` option to the URL of the included UploadProgress.php script, and `onProgress` will then return upload progress data in Internet Explorer 9 and below.
|
||||
|
||||
Note that this added functionality does not affect the behavior of the plugin for those not using PHP, or just not using the feature.
|
||||
|
||||
For those not using PHP, a similar result can still be achieved with the `startXH`/`endXHR` and `startNonXHR`/`endNonXHR` callback functions, which are included specifically for defining behavior based on whether XHR uploads are supported.
|
||||
|
||||
Also, adding support for other programming languages would certainly be a welcome addition, if anyone is interested in working on that.
|
||||
|
||||
Other items:
|
||||
|
||||
* Added `onUpdateFileSize` callback function for getting file size in IE9 and below (When server supported progress is enabled)
|
||||
* Removed the unneccessary _handleJSON method
|
||||
* Added new ss.newXHR method
|
||||
* Added extras folder for non-necessary items (i.e., everything but SimpleAjaxUploader.js)
|
||||
* Adjusted request headers for XHR uploads
|
||||
* Moved support detection for HTML5 File API to constructor so it only executes once
|
||||
* Timestamps now appended to URLs to prevent browsers from caching requests
|
||||
|
||||
### Version 1.3 ###
|
||||
* Returned to version numbering
|
||||
* Updated method for parsing JSON
|
||||
* Added PHP class for handling file uploads
|
||||
* Cleaned up messy areas
|
||||
|
||||
### Earlier versions ###
|
||||
Prior to version 1.3, I did a pretty horrible job of documenting changes, and, at one point, entirely dispensed with any notion of version control whatsoever. I have since seen the light.
|
||||
@@ -1,258 +0,0 @@
|
||||
Simple Ajax Uploader
|
||||
============================
|
||||
|
||||
A Javascript plugin for cross-browser Ajax file uploading. Supports drag and drop, CORS, and multiple file uploading with progress bars. Works in IE7-9, mobile, and all modern browsers.
|
||||
|
||||
```javascript
|
||||
var uploader = new ss.SimpleUpload({
|
||||
button: 'upload-btn', // HTML element used as upload button
|
||||
url: '/PathTo/UploadHandler', // URL of server-side upload handler
|
||||
name: 'uploadfile' // Parameter name of the uploaded file
|
||||
});
|
||||
```
|
||||
|
||||
### Features ###
|
||||
* Cross-browser -- works in IE7+, Firefox, Chrome, Safari, Opera
|
||||
* Supports multiple, concurrent file uploads (even in non-HTML5 browsers)
|
||||
* Built-in CORS support
|
||||
* Drag and drop file uploads (<strong>new in v2.0</strong>)
|
||||
* No flash or external CSS -- a single 6Kb Javascript file (minified and gzipped)
|
||||
* Progress bars in all browsers, including IE9 and older. Built-in support for:
|
||||
* <a href="http://wiki.nginx.org/HttpUploadProgressModule">Nginx Upload Progress Module</a>
|
||||
* <a href="http://www.php.net/manual/en/apc.configuration.php#ini.apc.rfc1867">PHP APC File Upload Progress</a>
|
||||
* <a href="http://php.net/manual/en/session.upload-progress.php">PHP Session Upload Progress</a>
|
||||
* Use any HTML element as the upload button
|
||||
* No dependencies - use it with or without jQuery
|
||||
* Provides individual callback functions for XHR-supported browsers and for browsers that do not support XHR uploads
|
||||
* Ability to pass custom headers in request such as the Authorization header
|
||||
|
||||
### How to Use ###
|
||||
|
||||
<a href="https://www.lpology.com/code/ajaxuploader/">Live Demo</a><br />
|
||||
<a href="https://www.lpology.com/code/ajaxuploader/docs.php">API Reference</a><br />
|
||||
<a href="https://www.lpology.com/code/ajaxuploader/progress.php">Upload progress bars in IE9 (and older)</a><br />
|
||||
<a href="https://www.lpology.com/code/ajaxuploader/How-to-Cross-Domain-File-Uploading.php">CORS — Cross-domain file uploading with Simple Ajax Uploader</a>
|
||||
|
||||
There are two main ways to use the plugin:
|
||||
|
||||
<strong>1. Single file uploading</strong> - Only one upload allowed at a time. Progress bar is an element that is re-used for each upload.<br />
|
||||
<strong>2. Multiple file uploading</strong> - Allow multiple, concurrent file uploads. Progress bars are created on the fly before each upload.
|
||||
|
||||
#### Method 1: Single file uploading (one file at a time) ####
|
||||
|
||||
Before each upload, in the `onSubmit()` callback function, the on-page <code>sizeBox</code> and <code>progress</code> elements are assigned specific roles using these two functions:
|
||||
|
||||
`setProgressBar(elem)` - Designates an element as the progress bar for an upload.<br />
|
||||
`setFileSizeBox(elem)` - Designates an element as the container in which the file size of an uploading file will be inserted.
|
||||
|
||||
As a result, when an upload begins, the file size of the upload is inserted into the <code>sizeBox</code> element and the CSS width of the <code>progress</code> element is set to 0%. As the upload progresses, the CSS width percentage of the <code>progress</code> element will be updated accordingly.
|
||||
|
||||
This approach of assigning roles to elements provides developers with a great deal of flexibility -- progress indicators can be styled in any way and placed anywhere on the page.
|
||||
|
||||
```javascript
|
||||
var sizeBox = document.getElementById('sizeBox'), // container for file size info
|
||||
progress = document.getElementById('progress'); // the element we're using for a progress bar
|
||||
|
||||
var uploader = new ss.SimpleUpload({
|
||||
button: 'uploadButton', // file upload button
|
||||
url: 'uploadHandler.php', // server side handler
|
||||
name: 'uploadfile', // upload parameter name
|
||||
progressUrl: 'uploadProgress.php', // enables cross-browser progress support (more info below)
|
||||
responseType: 'json',
|
||||
allowedExtensions: ['jpg', 'jpeg', 'png', 'gif'],
|
||||
maxSize: 1024, // kilobytes
|
||||
hoverClass: 'ui-state-hover',
|
||||
focusClass: 'ui-state-focus',
|
||||
disabledClass: 'ui-state-disabled',
|
||||
onSubmit: function(filename, extension) {
|
||||
this.setFileSizeBox(sizeBox); // designate this element as file size container
|
||||
this.setProgressBar(progress); // designate as progress bar
|
||||
},
|
||||
onComplete: function(filename, response) {
|
||||
if (!response) {
|
||||
alert(filename + 'upload failed');
|
||||
return false;
|
||||
}
|
||||
// do something with response...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Method 2: Multiple file uploads ####
|
||||
|
||||
Below is an example of how to implement multiple file uploading with progress bars. A new progress bar is created for each file upload within the `onSubmit()` callback function.
|
||||
|
||||
Like in Method 1, the newly created elements are assigned roles using the `setProgressBar()` and `setFileSizeBox()` functions. Unlike the previous example, however, the progress elements are automatically removed when the upload is completed.
|
||||
|
||||
```javascript
|
||||
var uploader = new ss.SimpleUpload({
|
||||
button: 'uploadButton',
|
||||
url: 'uploadHandler.php', // server side handler
|
||||
progressUrl: 'uploadProgress.php', // enables cross-browser progress support (more info below)
|
||||
responseType: 'json',
|
||||
name: 'uploadfile',
|
||||
multiple: true,
|
||||
allowedExtensions: ['jpg', 'jpeg', 'png', 'gif'], // for example, if we were uploading pics
|
||||
hoverClass: 'ui-state-hover',
|
||||
focusClass: 'ui-state-focus',
|
||||
disabledClass: 'ui-state-disabled',
|
||||
onSubmit: function(filename, extension) {
|
||||
// Create the elements of our progress bar
|
||||
var progress = document.createElement('div'), // container for progress bar
|
||||
bar = document.createElement('div'), // actual progress bar
|
||||
fileSize = document.createElement('div'), // container for upload file size
|
||||
wrapper = document.createElement('div'), // container for this progress bar
|
||||
progressBox = document.getElementById('progressBox'); // on page container for progress bars
|
||||
|
||||
// Assign each element its corresponding class
|
||||
progress.className = 'progress';
|
||||
bar.className = 'bar';
|
||||
fileSize.className = 'size';
|
||||
wrapper.className = 'wrapper';
|
||||
|
||||
// Assemble the progress bar and add it to the page
|
||||
progress.appendChild(bar);
|
||||
wrapper.innerHTML = '<div class="name">'+filename+'</div>'; // filename is passed to onSubmit()
|
||||
wrapper.appendChild(fileSize);
|
||||
wrapper.appendChild(progress);
|
||||
progressBox.appendChild(wrapper); // just an element on the page to hold the progress bars
|
||||
|
||||
// Assign roles to the elements of the progress bar
|
||||
this.setProgressBar(bar); // will serve as the actual progress bar
|
||||
this.setFileSizeBox(fileSize); // display file size beside progress bar
|
||||
this.setProgressContainer(wrapper); // designate the containing div to be removed after upload
|
||||
},
|
||||
|
||||
// Do something after finishing the upload
|
||||
// Note that the progress bar will be automatically removed upon completion because everything
|
||||
// is encased in the "wrapper", which was designated to be removed with setProgressContainer()
|
||||
onComplete: function(filename, response) {
|
||||
if (!response) {
|
||||
alert(filename + 'upload failed');
|
||||
return false;
|
||||
}
|
||||
// Stuff to do after finishing an upload...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
For multiple file uploads, we use an additional function: `setProgressContainer(elem)`. This function designates an element to be removed from the DOM after the upload is completed.
|
||||
|
||||
In the example, the element set to be removed with `setProgressContainer()` is the outer container for the progress elements. As a result, progress bars will be removed from the DOM after each upload is completed.
|
||||
|
||||
### Cross-Browser Helper Functions ###
|
||||
|
||||
To ease the pain of supporting older browsers, the plugin includes a set of callback functions which allow specific behavior to be defined based on whether the user's browser supports XHR uploads/HTML5 File API:
|
||||
|
||||
<code>startXHR(filename, fileSize)</code> - Called prior to upload -- only in browsers that support XHR uploads<br />
|
||||
<code>endXHR(filename)</code> - Called after upload is completed -- only in browsers that support XHR uploads<br />
|
||||
<code>startNonXHR(filename)</code> - Called prior to upload -- only in browsers that <strong>do not</strong> support XHR uploads<br />
|
||||
<code>endNonXHR(filename)</code> - Called after upload is completed -- only in browsers that <strong>do not</strong> support XHR uploads<br />
|
||||
|
||||
A common use case is to show an upload progress bar in browsers that support the <code>progress</code> event while displaying an animated GIF in older browsers:
|
||||
|
||||
```javascript
|
||||
|
||||
var progress = document.getElementById('progress'), // progress bar
|
||||
loaderImg = document.getElementById('loaderImg'); // "loading" animated GIF
|
||||
|
||||
var uploader = new ss.SimpleUpload({
|
||||
button: 'uploadButton',
|
||||
url: 'uploadHandler.php', // server side handler
|
||||
responseType: 'json',
|
||||
name: 'uploadfile',
|
||||
hoverClass: 'ui-state-hover',
|
||||
focusClass: 'ui-state-focus',
|
||||
disabledClass: 'ui-state-disabled',
|
||||
startXHR: function(filename, size) {
|
||||
progress.style.display = 'inline-block'; // show progress bar
|
||||
this.setProgressBar(progress); // designate as progress bar
|
||||
},
|
||||
endXHR: function(filename) {
|
||||
progress.style.display = 'none'; // hide progress bar
|
||||
},
|
||||
startNonXHR: function(filename) {
|
||||
loaderImg.style.display = 'inline-block'; // show animated GIF
|
||||
},
|
||||
endNonXHR: function(filename) {
|
||||
loaderImg.style.display = 'none'; // hide animated GIF
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Returning <code>false</code> from <code>startXHR()</code> and <code>startNonXHR()</code> will prevent the upload from starting, just as it does with <code>onSubmit()</code> and <code>onChange()</code>.
|
||||
|
||||
### Server-side file handling ###
|
||||
Files are uploaded by POST as either raw form data or regular multipart/form-data, depending on the browser.
|
||||
|
||||
### Using Uploader.php ###
|
||||
|
||||
<strong>Note:</strong> This PHP class is included only for convenience. <strong>It is not required to use PHP with Simple Ajax Uploader.</strong> The plugin is agnostic to server configuration, so use any language you prefer.
|
||||
|
||||
```php
|
||||
<?php
|
||||
require('Uploader.php');
|
||||
|
||||
$upload_dir = '/img_uploads/';
|
||||
$valid_extensions = array('gif', 'png', 'jpeg', 'jpg');
|
||||
|
||||
$Upload = new FileUpload('uploadfile');
|
||||
$result = $Upload->handleUpload($upload_dir, $valid_extensions);
|
||||
|
||||
if (!$result) {
|
||||
echo json_encode(array('success' => false, 'msg' => $Upload->getErrorMsg()));
|
||||
} else {
|
||||
echo json_encode(array('success' => true, 'file' => $Upload->getFileName()));
|
||||
}
|
||||
```
|
||||
|
||||
You can also save the uploaded file with a different name by setting the `newFileName` property:
|
||||
|
||||
```php
|
||||
$Upload = new FileUpload('uploadfile');
|
||||
$ext = $Upload->getExtension(); // Get the extension of the uploaded file
|
||||
$Upload->newFileName = 'customFileName.'.$ext;
|
||||
$result = $Upload->handleUpload($upload_dir, $valid_extensions);
|
||||
```
|
||||
|
||||
To access the newly uploaded file, use the `getSavedFile()` method to get the file's path after the upload is completed:
|
||||
```php
|
||||
$Upload = new FileUpload('uploadfile');
|
||||
$result = $Upload->handleUpload($upload_dir, $valid_extensions);
|
||||
|
||||
if ($result) {
|
||||
$path = $Upload->getSavedFile();
|
||||
$imgsize = getimagesize($path);
|
||||
// image resizing stuff...
|
||||
}
|
||||
```
|
||||
|
||||
### Passing Custom Headers ###
|
||||
|
||||
```javascript
|
||||
var uploader = new ss.SimpleUpload({
|
||||
customHeaders: {'Authorization': 'my-access-token'},
|
||||
...
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Drag and Drop ###
|
||||
|
||||
Enable drag and drop uploading by passing an element to the `dropzone` option to serve as the drop zone:
|
||||
|
||||
```javascript
|
||||
var uploader = new ss.SimpleUpload({
|
||||
dropzone: 'dragbox', // ID of element to be the drop zone
|
||||
url: 'uploadHandler.php',
|
||||
name: 'uploadfile',
|
||||
responseType: 'json',
|
||||
onComplete: function(filename, response) {
|
||||
// do something with response...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### License ###
|
||||
Released under the MIT license.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,254 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Simple Ajax Uploader
|
||||
* Version 2.0
|
||||
* https://github.com/LPology/Simple-Ajax-Uploader
|
||||
*
|
||||
* Copyright 2012-2015 LPology, LLC
|
||||
* Released under the MIT license
|
||||
*
|
||||
* View the documentation for an example of how to use this class.
|
||||
*/
|
||||
|
||||
class FileUpload {
|
||||
private $fileName; // Filename of the uploaded file
|
||||
private $fileSize; // Size of uploaded file in bytes
|
||||
private $fileExtension; // File extension of uploaded file
|
||||
private $fileNameWithoutExt;
|
||||
private $savedFile; // Path to newly uploaded file (after upload completed)
|
||||
private $errorMsg; // Error message if handleUpload() returns false (use getErrorMsg() to retrieve)
|
||||
private $isXhr;
|
||||
public $uploadDir; // File upload directory (include trailing slash)
|
||||
public $allowedExtensions; // Array of permitted file extensions
|
||||
public $sizeLimit = 10485760; // Max file upload size in bytes (default 10MB)
|
||||
public $newFileName; // Optionally save uploaded files with a new name by setting this
|
||||
public $corsInputName = 'XHR_CORS_TARGETORIGIN';
|
||||
public $uploadName = 'uploadfile';
|
||||
|
||||
function __construct($uploadName = null) {
|
||||
if ($uploadName !== null) {
|
||||
$this->uploadName = $uploadName;
|
||||
}
|
||||
|
||||
if (isset($_FILES[$this->uploadName])) {
|
||||
$this->isXhr = false;
|
||||
|
||||
if ($_FILES[$this->uploadName]['error'] === UPLOAD_ERR_OK) {
|
||||
$this->fileName = $_FILES[$this->uploadName]['name'];
|
||||
$this->fileSize = $_FILES[$this->uploadName]['size'];
|
||||
|
||||
} else {
|
||||
$this->setErrorMsg($this->errorCodeToMsg($_FILES[$this->uploadName]['error']));
|
||||
}
|
||||
|
||||
} elseif (isset($_SERVER['HTTP_X_FILE_NAME']) || isset($_GET[$this->uploadName])) {
|
||||
$this->isXhr = true;
|
||||
|
||||
$this->fileName = isset($_SERVER['HTTP_X_FILE_NAME']) ?
|
||||
$_SERVER['HTTP_X_FILE_NAME'] : $_GET[$this->uploadName];
|
||||
|
||||
if (isset($_SERVER['CONTENT_LENGTH'])) {
|
||||
$this->fileSize = (int)$_SERVER['CONTENT_LENGTH'];
|
||||
|
||||
} else {
|
||||
throw new Exception('Content length is empty.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->fileName) {
|
||||
$pathinfo = pathinfo($this->fileName);
|
||||
|
||||
if (array_key_exists('extension', $pathinfo) &&
|
||||
array_key_exists('filename', $pathinfo))
|
||||
{
|
||||
$this->fileExtension = strtolower($pathinfo['extension']);
|
||||
$this->fileNameWithoutExt = $pathinfo['filename'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getFileName() {
|
||||
return $this->fileName;
|
||||
}
|
||||
|
||||
public function getFileSize() {
|
||||
return $this->fileSize;
|
||||
}
|
||||
|
||||
public function getExtension() {
|
||||
return $this->fileExtension;
|
||||
}
|
||||
|
||||
public function getErrorMsg() {
|
||||
return $this->errorMsg;
|
||||
}
|
||||
|
||||
public function getSavedFile() {
|
||||
return $this->savedFile;
|
||||
}
|
||||
|
||||
private function errorCodeToMsg($code) {
|
||||
switch($code) {
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
$message = 'File size exceeds limit.';
|
||||
break;
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
$message = 'The uploaded file was only partially uploaded.';
|
||||
break;
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
$message = 'No file was uploaded.';
|
||||
break;
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
$message = 'Missing a temporary folder.';
|
||||
break;
|
||||
case UPLOAD_ERR_CANT_WRITE:
|
||||
$message = 'Failed to write file to disk.';
|
||||
break;
|
||||
case UPLOAD_ERR_EXTENSION:
|
||||
$message = 'File upload stopped by extension.';
|
||||
break;
|
||||
default:
|
||||
$message = 'Unknown upload error.';
|
||||
break;
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
private function checkExtension($ext, $allowedExtensions) {
|
||||
if (!is_array($allowedExtensions))
|
||||
return false;
|
||||
|
||||
if (!in_array(strtolower($ext), array_map('strtolower', $allowedExtensions)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function setErrorMsg($msg) {
|
||||
if (empty($this->errorMsg))
|
||||
$this->errorMsg = $msg;
|
||||
}
|
||||
|
||||
private function fixDir($dir) {
|
||||
if (empty($dir))
|
||||
return $dir;
|
||||
|
||||
$slash = DIRECTORY_SEPARATOR;
|
||||
$dir = str_replace('/', $slash, $dir);
|
||||
$dir = str_replace('\\', $slash, $dir);
|
||||
return substr($dir, -1) == $slash ? $dir : $dir . $slash;
|
||||
}
|
||||
|
||||
// escapeJS and jsMatcher are adapted from the Escaper component of
|
||||
// Zend Framework, Copyright (c) 2005-2013, Zend Technologies USA, Inc.
|
||||
// https://github.com/zendframework/zf2/tree/master/library/Zend/Escaper
|
||||
private function escapeJS($string) {
|
||||
return preg_replace_callback('/[^a-z0-9,\._]/iSu', $this->jsMatcher, $string);
|
||||
}
|
||||
|
||||
private function jsMatcher($matches) {
|
||||
$chr = $matches[0];
|
||||
|
||||
if (strlen($chr) == 1)
|
||||
return sprintf('\\x%02X', ord($chr));
|
||||
|
||||
if (function_exists('iconv'))
|
||||
$chr = iconv('UTF-16BE', 'UTF-8', $chr);
|
||||
|
||||
elseif (function_exists('mb_convert_encoding'))
|
||||
$chr = mb_convert_encoding($chr, 'UTF-8', 'UTF-16BE');
|
||||
|
||||
return sprintf('\\u%04s', strtoupper(bin2hex($chr)));
|
||||
}
|
||||
|
||||
public function corsResponse($data) {
|
||||
if (isset($_REQUEST[$this->corsInputName])) {
|
||||
$targetOrigin = $this->escapeJS($_REQUEST[$this->corsInputName]);
|
||||
$targetOrigin = htmlspecialchars($targetOrigin, ENT_QUOTES, 'UTF-8');
|
||||
return "<script>window.parent.postMessage('$data','$targetOrigin');</script>";
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function saveXhr($path) {
|
||||
if (false !== file_put_contents($path, fopen('php://input', 'r')))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private function saveForm($path) {
|
||||
if (move_uploaded_file($_FILES[$this->uploadName]['tmp_name'], $path))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private function save($path) {
|
||||
if (true === $this->isXhr)
|
||||
return $this->saveXhr($path);
|
||||
return $this->saveForm($path);
|
||||
}
|
||||
|
||||
public function handleUpload($uploadDir = null, $allowedExtensions = null) {
|
||||
if (!$this->fileName) {
|
||||
$this->setErrorMsg('Incorrect upload name or no file uploaded');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->fileSize == 0) {
|
||||
$this->setErrorMsg('File is empty');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->fileSize > $this->sizeLimit) {
|
||||
$this->setErrorMsg('File size exceeds limit');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($uploadDir))
|
||||
$this->uploadDir = $uploadDir;
|
||||
|
||||
$this->uploadDir = $this->fixDir($this->uploadDir);
|
||||
|
||||
if (!is_writable($this->uploadDir)) {
|
||||
$this->setErrorMsg('Upload directory is not writable');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_array($allowedExtensions))
|
||||
$this->allowedExtensions = $allowedExtensions;
|
||||
|
||||
if (!empty($this->allowedExtensions)) {
|
||||
if (!$this->checkExtension($this->fileExtension, $this->allowedExtensions)) {
|
||||
$this->setErrorMsg('Invalid file type');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->savedFile = $this->uploadDir . $this->fileName;
|
||||
|
||||
if (!empty($this->newFileName)) {
|
||||
$this->fileName = $this->newFileName;
|
||||
$this->savedFile = $this->uploadDir . $this->fileName;
|
||||
|
||||
$this->fileNameWithoutExt = null;
|
||||
$this->fileExtension = null;
|
||||
|
||||
$pathinfo = pathinfo($this->fileName);
|
||||
|
||||
if (array_key_exists('filename', $pathinfo))
|
||||
$this->fileNameWithoutExt = $pathinfo['filename'];
|
||||
|
||||
if (array_key_exists('extension', $pathinfo))
|
||||
$this->fileExtension = strtolower($pathinfo['extension']);
|
||||
}
|
||||
|
||||
if (!$this->save($this->savedFile)) {
|
||||
$this->setErrorMsg('File could not be saved');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
725
etc/apps/laragon/extras/assets/css/bootstrap.min.css
vendored
725
etc/apps/laragon/extras/assets/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -1,28 +0,0 @@
|
||||
.container {
|
||||
width: auto;
|
||||
max-width: 680px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-bottom:0;
|
||||
margin-top:6px;
|
||||
margin-left:10px;
|
||||
}
|
||||
|
||||
.btn.focus {
|
||||
outline:thin dotted #333;
|
||||
outline:5px auto -webkit-focus-ring-color;
|
||||
outline-offset:-2px;
|
||||
}
|
||||
|
||||
.btn.hover {
|
||||
color:#ffffff;
|
||||
background-color:#3276b1;
|
||||
border-color:#285e8e;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: yellow;
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Simple Ajax Uploader
|
||||
* Version 2.0
|
||||
* https://github.com/LPology/Simple-Ajax-Uploader
|
||||
*
|
||||
* Copyright 2012-2015 LPology, LLC
|
||||
* Released under the MIT license
|
||||
*
|
||||
*/
|
||||
|
||||
if (isset($_SERVER['HTTP_ORIGIN'])) {
|
||||
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
|
||||
header('Access-Control-Allow-Credentials: true');
|
||||
header('Access-Control-Max-Age: 86400'); // cache for 1 day
|
||||
}
|
||||
|
||||
// Access-Control headers are received during OPTIONS requests
|
||||
if (isset($_SERVER['REQUEST_METHOD'])) {
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
|
||||
|
||||
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
}
|
||||
|
||||
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
|
||||
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
require('Uploader.php');
|
||||
|
||||
// Directory where we're storing uploaded images
|
||||
// Remember to set correct permissions or it won't work
|
||||
$upload_dir = '../uploads';
|
||||
|
||||
$uploader = new FileUpload('uploadfile');
|
||||
$uploader->sizeLimit = 1024*1024*1024; // Max file upload size in bytes 1GB)
|
||||
|
||||
// Handle the upload
|
||||
$result = $uploader->handleUpload($upload_dir);
|
||||
|
||||
if (!$result) {
|
||||
exit(json_encode(array('success' => false, 'msg' => $uploader->getErrorMsg())));
|
||||
}
|
||||
|
||||
echo json_encode(array('success' => true));
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Simple Ajax Uploader
|
||||
* Version 2.0
|
||||
* https://github.com/LPology/Simple-Ajax-Uploader
|
||||
*
|
||||
* Copyright 2012-2015 LPology, LLC
|
||||
* Released under the MIT license
|
||||
*
|
||||
* Returns upload progress updates for browsers that don't support the HTML5 File API.
|
||||
* Falling back to this method allows for upload progress support across virtually all browsers.
|
||||
* Requires PHP 5.4+
|
||||
* Further documentation: http://php.net/manual/en/session.upload-progress.php
|
||||
*
|
||||
*/
|
||||
|
||||
session_start();
|
||||
|
||||
if (!isset($_POST[ini_get('session.upload_progress.name')])) {
|
||||
exit(json_encode(array('success' => false)));
|
||||
}
|
||||
|
||||
$key = ini_get('session.upload_progress.prefix') . $_POST[ini_get('session.upload_progress.name')];
|
||||
|
||||
if (!isset($_SESSION[$key])) {
|
||||
exit(json_encode(array('success' => false)));
|
||||
}
|
||||
|
||||
$progress = $_SESSION[$key];
|
||||
$pct = 0;
|
||||
$size = 0;
|
||||
|
||||
if (is_array($progress)) {
|
||||
|
||||
if (array_key_exists('bytes_processed', $progress) && array_key_exists('content_length', $progress)) {
|
||||
|
||||
if ($progress['content_length'] > 0) {
|
||||
$pct = round(($progress['bytes_processed'] / $progress['content_length']) * 100);
|
||||
$size = round($progress['content_length'] / 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(array('success' => true, 'pct' => $pct, 'size' => $size));
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Simple Ajax Uploader
|
||||
* Version 2.0
|
||||
* https://github.com/LPology/Simple-Ajax-Uploader
|
||||
*
|
||||
* Copyright 2012-2015 LPology, LLC
|
||||
* Released under the MIT license
|
||||
*
|
||||
* Returns upload progress updates for browsers that don't support the HTML5 File API.
|
||||
* Falling back to this method allows for upload progress support across virtually all browsers.
|
||||
*
|
||||
*/
|
||||
|
||||
// This "if" statement is only necessary for CORS uploads -- if you're
|
||||
// only doing same-domain uploads then you can delete it if you want
|
||||
if (isset($_SERVER['HTTP_ORIGIN'])) {
|
||||
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
|
||||
header('Access-Control-Allow-Credentials: true');
|
||||
header('Access-Control-Max-Age: 86400'); // cache for 1 day
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['progresskey'])) {
|
||||
$status = apc_fetch('upload_'.$_REQUEST['progresskey']);
|
||||
} else {
|
||||
exit(json_encode(array('success' => false)));
|
||||
}
|
||||
|
||||
$pct = 0;
|
||||
$size = 0;
|
||||
|
||||
if (is_array($status)) {
|
||||
|
||||
if (array_key_exists('total', $status) && array_key_exists('current', $status)) {
|
||||
|
||||
if ($status['total'] > 0) {
|
||||
$pct = round(($status['current'] / $status['total']) * 100);
|
||||
$size = round($status['total'] / 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(array('success' => true, 'pct' => $pct, 'size' => $size));
|
||||
@@ -1,154 +0,0 @@
|
||||
<?php
|
||||
function formatLink($file) {
|
||||
if (isset($_SERVER['HTTPS']) &&
|
||||
($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
|
||||
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
|
||||
$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
|
||||
$protocol = 'https';
|
||||
} else {
|
||||
$protocol = 'http';
|
||||
}
|
||||
$link = sprintf('%s://%s/laragon/uploads/%s', $protocol, $_SERVER['HTTP_HOST'], $file);
|
||||
return sprintf('<a href="%s" target="_blank">%s</a>', $link, $link);
|
||||
}
|
||||
function listFiles() {
|
||||
$upload_dir = dirname(__FILE__).'\uploads';
|
||||
echo sprintf('<div>Files locate at: <b>%s</b></div>', $upload_dir);
|
||||
if ($handle = opendir($upload_dir)) {
|
||||
|
||||
while (false !== ($entry = readdir($handle))) {
|
||||
if ($entry != "." && $entry != "..") {
|
||||
echo '<div>'.formatLink($entry).'</div>';
|
||||
}
|
||||
}
|
||||
|
||||
closedir($handle);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Uploader</title>
|
||||
<link href="extras/assets/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="extras/assets/css/styles.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<div class="row" style="padding-top:10px;">
|
||||
<div class="col-xs-2">
|
||||
<button id="uploadBtn" class="btn btn-large btn-primary">Choose Files</button>
|
||||
</div>
|
||||
<div class="col-xs-10">
|
||||
<div id="progressOuter" class="progress progress-striped active" style="display:none;">
|
||||
<div id="progressBar" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="padding-top:10px;">
|
||||
<div class="col-xs-10">
|
||||
<div id="msgBox">
|
||||
</div>
|
||||
<div id="uploadedFiles">
|
||||
</div>
|
||||
<p>
|
||||
<hr />
|
||||
<div>
|
||||
<small>
|
||||
<div><u>Tip</u>: Hold Shift if you want to upload multiple files.</div>
|
||||
<div>If you want to share over the Internet, just run:</div>
|
||||
<div><b><i>ngrok http 80</i></b></div>
|
||||
</small>
|
||||
</div>
|
||||
<hr />
|
||||
<div id="listFiles">
|
||||
<?php
|
||||
listFiles();
|
||||
|
||||
//$upload_dir = dirname(__FILE__).'\uploads';
|
||||
//echo $upload_dir;
|
||||
//echo '<a href="http://'.$_SERVER['HTTP_HOST'].'/laragon/uploads/" target="_blank">'.$_SERVER['HTTP_HOST'].'</a>';
|
||||
?>
|
||||
</div>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="extras/SimpleAjaxUploader.min.js"></script>
|
||||
<script>
|
||||
function escapeTags( str ) {
|
||||
return String( str )
|
||||
.replace( /&/g, '&' )
|
||||
.replace( /"/g, '"' )
|
||||
.replace( /'/g, ''' )
|
||||
.replace( /</g, '<' )
|
||||
.replace( />/g, '>' );
|
||||
}
|
||||
|
||||
function replaceAll(str, find, replace) {
|
||||
return str.replace(new RegExp(find, 'g'), replace);
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
|
||||
var btn = document.getElementById('uploadBtn'),
|
||||
progressBar = document.getElementById('progressBar'),
|
||||
progressOuter = document.getElementById('progressOuter'),
|
||||
msgBox = document.getElementById('msgBox');
|
||||
listFiles = document.getElementById('listFiles');
|
||||
theLink = '<?php echo formatLink('{{FILE}}'); ?>';
|
||||
|
||||
var uploader = new ss.SimpleUpload({
|
||||
button: btn,
|
||||
url: 'extras/file_upload.php',
|
||||
name: 'uploadfile',
|
||||
hoverClass: 'hover',
|
||||
multiple: true,
|
||||
focusClass: 'focus',
|
||||
responseType: 'json',
|
||||
startXHR: function() {
|
||||
progressOuter.style.display = 'block'; // make progress bar visible
|
||||
this.setProgressBar( progressBar );
|
||||
},
|
||||
onSubmit: function() {
|
||||
msgBox.innerHTML = ''; // empty the message box
|
||||
btn.innerHTML = 'Uploading...'; // change button text to "Uploading..."
|
||||
},
|
||||
onComplete: function( filename, response ) {
|
||||
btn.innerHTML = 'Choose Files';
|
||||
progressOuter.style.display = 'none'; // hide progress bar when upload is completed
|
||||
|
||||
if ( !response ) {
|
||||
msgBox.innerHTML = 'Unable to upload file';
|
||||
return;
|
||||
}
|
||||
|
||||
if ( response.success === true ) {
|
||||
msgBox.innerHTML = '<strong>' + escapeTags( filename ) + '</strong>' + ' successfully uploaded.';
|
||||
uploadedFiles.innerHTML = '<div class="highlight">' + replaceAll(theLink, '{{FILE}}', filename) + '</div>' + uploadedFiles.innerHTML ;
|
||||
|
||||
} else {
|
||||
if ( response.msg ) {
|
||||
msgBox.innerHTML = escapeTags( response.msg );
|
||||
|
||||
} else {
|
||||
msgBox.innerHTML = 'An error occurred and the upload failed.';
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: function() {
|
||||
progressOuter.style.display = 'none';
|
||||
msgBox.innerHTML = 'Unable to upload file';
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
Antony Dovgal, Mikael Johansson
|
||||
@@ -1,68 +0,0 @@
|
||||
--------------------------------------------------------------------
|
||||
The PHP License, Version 3.0
|
||||
Copyright (c) 1999 - 2005 The PHP Group. All rights reserved.
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. The name "PHP" must not be used to endorse or promote products
|
||||
derived from this software without prior written permission. For
|
||||
written permission, please contact group@php.net.
|
||||
|
||||
4. Products derived from this software may not be called "PHP", nor
|
||||
may "PHP" appear in their name, without prior written permission
|
||||
from group@php.net. You may indicate that your software works in
|
||||
conjunction with PHP by saying "Foo for PHP" instead of calling
|
||||
it "PHP Foo" or "phpfoo"
|
||||
|
||||
5. The PHP Group may publish revised and/or new versions of the
|
||||
license from time to time. Each version will be given a
|
||||
distinguishing version number.
|
||||
Once covered code has been published under a particular version
|
||||
of the license, you may always continue to use it under the terms
|
||||
of that version. You may also choose to use such covered code
|
||||
under the terms of any subsequent version of the license
|
||||
published by the PHP Group. No one other than the PHP Group has
|
||||
the right to modify the terms applicable to covered code created
|
||||
under this License.
|
||||
|
||||
6. Redistributions of any form whatsoever must retain the following
|
||||
acknowledgment:
|
||||
"This product includes PHP, freely available from
|
||||
<http://www.php.net/>".
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
|
||||
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
|
||||
DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals on behalf of the PHP Group.
|
||||
|
||||
The PHP Group can be contacted via Email at group@php.net.
|
||||
|
||||
For more information on the PHP Group and the PHP project,
|
||||
please see <http://www.php.net>.
|
||||
|
||||
This product includes the Zend Engine, freely available at
|
||||
<http://www.zend.com>.
|
||||
@@ -1,149 +0,0 @@
|
||||
memcached module for PHP
|
||||
------------------------
|
||||
This module requires zlib library, used for on-the-fly data (de)compression.
|
||||
Also, you'll need memcached to use it =)
|
||||
|
||||
The memcached website is here:
|
||||
http://www.danga.com/memcached/
|
||||
|
||||
You will probably need libevent to install memcached:
|
||||
You can download it here: http://www.monkey.org/~provos/libevent/
|
||||
|
||||
How to run tests:
|
||||
1. sh tests/memcache.sh
|
||||
2. TEST_PHP_EXECUTABLE=/usr/local/bin/php php -dextension=modules/memcache.so run-tests.php -d extension=modules/memcache.so
|
||||
|
||||
|
||||
New API in 3.0
|
||||
------------------------
|
||||
|
||||
Version 3 introduces a new class "MemcachePool" which implements the new API, the
|
||||
old class "Memcache" is still retained (but is deprecated) with the same interface
|
||||
for backwards compatibility. Please note that you need a new memcached version to
|
||||
use the CAS, default value to increment/decrement, append and prepend, and binary
|
||||
protocol features.
|
||||
|
||||
New INI directives are available to allow control over protocol, redundancy and hash
|
||||
strategy selection. These are
|
||||
|
||||
# The binary protocol results in less traffic and is more efficient
|
||||
# for the client and server to generate/parse
|
||||
|
||||
memcache.protocol = {ascii, binary} # default ascii
|
||||
|
||||
# When enabled the client sends requests to N servers in parallel, resulting in
|
||||
# a somewhat crude reduncancy or mirroring, suitable when used as a session
|
||||
# storage.
|
||||
#
|
||||
# If data integrity is of greater importance a real replicating memcached
|
||||
# backend such as "repcached" (http://sourceforge.net/projects/repcached/) is
|
||||
# recommended
|
||||
|
||||
memcache.redundancy = <int> # default 1
|
||||
memcache.session_redundancy = <int> # default 2
|
||||
|
||||
# Hash strategy and function selection. The consistent hashing strategy
|
||||
# is now the default as it allows servers to be added and removed from
|
||||
# the pool without resulting in all or most keys being re-mapped to
|
||||
# other server (ie. voiding the cache)
|
||||
|
||||
memcache.hash_strategy = {standard, consistent} # default consistent
|
||||
memcache.hash_function = {crc32, fnv} # default crc32
|
||||
|
||||
# Compression is enabled by default, the threshold which control the minimum
|
||||
# string length which triggers compresssion can be changed as
|
||||
|
||||
memcache.compress_threshold = <int> # default 20000
|
||||
|
||||
|
||||
The directives are used by the MemcachePool constructor so you can instantiate
|
||||
several pools with different settings by using ini_set() creativly. For example
|
||||
|
||||
ini_set('memcache.protocol', 'binary');
|
||||
|
||||
$binarypool = new MemcachePool();
|
||||
$binarypool->addServer(...)
|
||||
|
||||
ini_set('memcache.protocol', 'ascii');
|
||||
ini_set('memcache.redundancy', '2');
|
||||
|
||||
$redundantpool = new MemcachePool();
|
||||
$redundantpool->addServer(...)
|
||||
|
||||
ini_set('memcache.redundancy', '1');
|
||||
|
||||
|
||||
The new interface looks like
|
||||
|
||||
class MemcachePool() {
|
||||
bool connect(string host, int tcp_port = 11211, int udp_port = 0, bool persistent = true, int weight = 1, int timeout = 1, int retry_interval = 15)
|
||||
bool addServer(string host, int tcp_port = 11211, int udp_port = 0, bool persistent = true, int weight = 1, int timeout = 1, int retry_interval = 15, bool status = true)
|
||||
bool setServerParams(string host, int tcp_port = 11211, int timeout = 1, int retry_interval = 15, bool status = true)
|
||||
|
||||
/**
|
||||
* Supports fetching flags and CAS values
|
||||
*/
|
||||
mixed get(mixed key, mixed &flags = null, mixed &cas = null)
|
||||
|
||||
/**
|
||||
* Supports multi-set, for example
|
||||
* $memcache->set(array('key1' => 'val1', 'key2' => 'val1'), null, 0, 60)
|
||||
*/
|
||||
bool add(mixed key, mixed var = null, int flag = 0, int exptime = 0)
|
||||
bool set(mixed key, mixed var = null, int flag = 0, int exptime = 0)
|
||||
bool replace(mixed key, mixed var = null, int flag = 0, int exptime = 0)
|
||||
|
||||
/**
|
||||
* Compare-and-Swap, uses the CAS param from MemcachePool::get()
|
||||
*/
|
||||
bool cas(mixed key, mixed var = null, int flag = 0, int exptime = 0, int cas = 0)
|
||||
|
||||
/**
|
||||
* Prepends/appends a value to an existing one
|
||||
*/
|
||||
bool append(mixed key, mixed var = null, int flag = 0, int exptime = 0)
|
||||
bool prepend(mixed key, mixed var = null, int flag = 0, int exptime = 0)
|
||||
|
||||
/**
|
||||
* Supports multi-key operations, for example
|
||||
* $memcache->delete(array('key1', 'key2'))
|
||||
*/
|
||||
bool delete(mixed key, int exptime = 0)
|
||||
|
||||
/**
|
||||
* Supports multi-key operations, for example
|
||||
* $memcache->increment(array('key1', 'key2'), 1, 0, 0)
|
||||
*
|
||||
* The new defval (default value) and exptime (expiration time) are used
|
||||
* if the key doesn't already exist. They must be supplied (even if 0) for
|
||||
* this to be enabled.
|
||||
*
|
||||
* Returns an integer with the new value if key is a string
|
||||
* Returns an array of integers if the key is an array
|
||||
*/
|
||||
mixed increment(mixed key, int value = 1, int defval = 0, int exptime = 0)
|
||||
mixed decrement(mixed key, int value = 1, int defval = 0, int exptime = 0)
|
||||
|
||||
/**
|
||||
* Assigns a pool-specific failure callback which will be called when
|
||||
* a request fails. May be null in order to disable callbacks. The callback
|
||||
* receive arguments like
|
||||
*
|
||||
* function mycallback($host, $tcp_port, $udp_port, $error, $errnum)
|
||||
*
|
||||
* Where $host and $error are strings or null, the other params are integers.
|
||||
*/
|
||||
bool setFailureCallback(function callback)
|
||||
|
||||
/**
|
||||
* Locates the server a given would be hashed to
|
||||
*
|
||||
* Returns a string "hostname:port" on success
|
||||
* Returns false on failure such as invalid key
|
||||
*/
|
||||
string findServer(string key)
|
||||
}
|
||||
|
||||
|
||||
Maintainers:
|
||||
Herman J. Radtke III hradtke at php dot net
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
$memcache = memcache_connect('localhost', 11211);
|
||||
|
||||
if ($memcache) {
|
||||
$memcache->set("str_key", "String to store in memcached");
|
||||
$memcache->set("num_key", 123);
|
||||
|
||||
$object = new StdClass;
|
||||
$object->attribute = 'test';
|
||||
$memcache->set("obj_key", $object);
|
||||
|
||||
$array = Array('assoc'=>123, 345, 567);
|
||||
$memcache->set("arr_key", $array);
|
||||
|
||||
var_dump($memcache->get('str_key'));
|
||||
var_dump($memcache->get('num_key'));
|
||||
var_dump($memcache->get('obj_key'));
|
||||
}
|
||||
else {
|
||||
echo "Connection to memcached failed";
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,900 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2004 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.0 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_0.txt. |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Harun Yayli <harunyayli at gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
$VERSION='$Id: memcache.php 326707 2012-07-19 19:02:42Z ab $';
|
||||
|
||||
define('ADMIN_USERNAME','mem'); // Admin Username
|
||||
define('ADMIN_PASSWORD','1'); // Admin Password
|
||||
define('DATE_FORMAT','Y/m/d H:i:s');
|
||||
define('GRAPH_SIZE',200);
|
||||
define('MAX_ITEM_DUMP',50);
|
||||
|
||||
$MEMCACHE_SERVERS[] = 'localhost:11211'; // add more as an array
|
||||
//$MEMCACHE_SERVERS[] = 'mymemcache-server2:11211'; // add more as an array
|
||||
|
||||
|
||||
////////// END OF DEFAULT CONFIG AREA /////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////// Password protect ////////////////////////////////////////////////////////////////
|
||||
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) ||
|
||||
$_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME ||$_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) {
|
||||
Header("WWW-Authenticate: Basic realm=\"Memcache Login (Username: ".ADMIN_USERNAME."/Password: ".ADMIN_PASSWORD.")\"");
|
||||
Header("HTTP/1.0 401 Unauthorized");
|
||||
|
||||
echo <<<EOB
|
||||
<html><body>
|
||||
<h1>Rejected!</h1>
|
||||
<big>Wrong Username or Password!</big>
|
||||
</body></html>
|
||||
EOB;
|
||||
exit;
|
||||
}
|
||||
|
||||
///////////MEMCACHE FUNCTIONS /////////////////////////////////////////////////////////////////////
|
||||
|
||||
function get_host_port_from_server($server){
|
||||
$values = explode(':', $server);
|
||||
if (($values[0] == 'unix') && (!is_numeric( $values[1]))) {
|
||||
return array($server, 0);
|
||||
}
|
||||
else {
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
|
||||
function sendMemcacheCommands($command){
|
||||
global $MEMCACHE_SERVERS;
|
||||
$result = array();
|
||||
|
||||
foreach($MEMCACHE_SERVERS as $server){
|
||||
$strs = get_host_port_from_server($server);
|
||||
$host = $strs[0];
|
||||
$port = $strs[1];
|
||||
$result[$server] = sendMemcacheCommand($host,$port,$command);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
function sendMemcacheCommand($server,$port,$command){
|
||||
|
||||
$s = @fsockopen($server,$port);
|
||||
if (!$s){
|
||||
die("Cant connect to:".$server.':'.$port);
|
||||
}
|
||||
|
||||
fwrite($s, $command."\r\n");
|
||||
|
||||
$buf='';
|
||||
while ((!feof($s))) {
|
||||
$buf .= fgets($s, 256);
|
||||
if (strpos($buf,"END\r\n")!==false){ // stat says end
|
||||
break;
|
||||
}
|
||||
if (strpos($buf,"DELETED\r\n")!==false || strpos($buf,"NOT_FOUND\r\n")!==false){ // delete says these
|
||||
break;
|
||||
}
|
||||
if (strpos($buf,"OK\r\n")!==false){ // flush_all says ok
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose($s);
|
||||
return parseMemcacheResults($buf);
|
||||
}
|
||||
function parseMemcacheResults($str){
|
||||
|
||||
$res = array();
|
||||
$lines = explode("\r\n",$str);
|
||||
$cnt = count($lines);
|
||||
for($i=0; $i< $cnt; $i++){
|
||||
$line = $lines[$i];
|
||||
$l = explode(' ',$line,3);
|
||||
if (count($l)==3){
|
||||
$res[$l[0]][$l[1]]=$l[2];
|
||||
if ($l[0]=='VALUE'){ // next line is the value
|
||||
$res[$l[0]][$l[1]] = array();
|
||||
list ($flag,$size)=explode(' ',$l[2]);
|
||||
$res[$l[0]][$l[1]]['stat']=array('flag'=>$flag,'size'=>$size);
|
||||
$res[$l[0]][$l[1]]['value']=$lines[++$i];
|
||||
}
|
||||
}elseif($line=='DELETED' || $line=='NOT_FOUND' || $line=='OK'){
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
|
||||
}
|
||||
|
||||
function dumpCacheSlab($server,$slabId,$limit){
|
||||
list($host,$port) = get_host_port_from_server($server);
|
||||
$resp = sendMemcacheCommand($host,$port,'stats cachedump '.$slabId.' '.$limit);
|
||||
|
||||
return $resp;
|
||||
|
||||
}
|
||||
|
||||
function flushServer($server){
|
||||
list($host,$port) = get_host_port_from_server($server);
|
||||
$resp = sendMemcacheCommand($host,$port,'flush_all');
|
||||
return $resp;
|
||||
}
|
||||
function getCacheItems(){
|
||||
$items = sendMemcacheCommands('stats items');
|
||||
$serverItems = array();
|
||||
$totalItems = array();
|
||||
foreach ($items as $server=>$itemlist){
|
||||
$serverItems[$server] = array();
|
||||
$totalItems[$server]=0;
|
||||
if (!isset($itemlist['STAT'])){
|
||||
continue;
|
||||
}
|
||||
|
||||
$iteminfo = $itemlist['STAT'];
|
||||
|
||||
foreach($iteminfo as $keyinfo=>$value){
|
||||
if (preg_match('/items\:(\d+?)\:(.+?)$/',$keyinfo,$matches)){
|
||||
$serverItems[$server][$matches[1]][$matches[2]] = $value;
|
||||
if ($matches[2]=='number'){
|
||||
$totalItems[$server] +=$value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return array('items'=>$serverItems,'counts'=>$totalItems);
|
||||
}
|
||||
function getMemcacheStats($total=true){
|
||||
$resp = sendMemcacheCommands('stats');
|
||||
if ($total){
|
||||
$res = array();
|
||||
foreach($resp as $server=>$r){
|
||||
foreach($r['STAT'] as $key=>$row){
|
||||
if (!isset($res[$key])){
|
||||
$res[$key]=null;
|
||||
}
|
||||
switch ($key){
|
||||
case 'pid':
|
||||
$res['pid'][$server]=$row;
|
||||
break;
|
||||
case 'uptime':
|
||||
$res['uptime'][$server]=$row;
|
||||
break;
|
||||
case 'time':
|
||||
$res['time'][$server]=$row;
|
||||
break;
|
||||
case 'version':
|
||||
$res['version'][$server]=$row;
|
||||
break;
|
||||
case 'pointer_size':
|
||||
$res['pointer_size'][$server]=$row;
|
||||
break;
|
||||
case 'rusage_user':
|
||||
$res['rusage_user'][$server]=$row;
|
||||
break;
|
||||
case 'rusage_system':
|
||||
$res['rusage_system'][$server]=$row;
|
||||
break;
|
||||
case 'curr_items':
|
||||
$res['curr_items']+=$row;
|
||||
break;
|
||||
case 'total_items':
|
||||
$res['total_items']+=$row;
|
||||
break;
|
||||
case 'bytes':
|
||||
$res['bytes']+=$row;
|
||||
break;
|
||||
case 'curr_connections':
|
||||
$res['curr_connections']+=$row;
|
||||
break;
|
||||
case 'total_connections':
|
||||
$res['total_connections']+=$row;
|
||||
break;
|
||||
case 'connection_structures':
|
||||
$res['connection_structures']+=$row;
|
||||
break;
|
||||
case 'cmd_get':
|
||||
$res['cmd_get']+=$row;
|
||||
break;
|
||||
case 'cmd_set':
|
||||
$res['cmd_set']+=$row;
|
||||
break;
|
||||
case 'get_hits':
|
||||
$res['get_hits']+=$row;
|
||||
break;
|
||||
case 'get_misses':
|
||||
$res['get_misses']+=$row;
|
||||
break;
|
||||
case 'evictions':
|
||||
$res['evictions']+=$row;
|
||||
break;
|
||||
case 'bytes_read':
|
||||
$res['bytes_read']+=$row;
|
||||
break;
|
||||
case 'bytes_written':
|
||||
$res['bytes_written']+=$row;
|
||||
break;
|
||||
case 'limit_maxbytes':
|
||||
$res['limit_maxbytes']+=$row;
|
||||
break;
|
||||
case 'threads':
|
||||
$res['rusage_system'][$server]=$row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
return $resp;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// don't cache this page
|
||||
//
|
||||
header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
|
||||
header("Cache-Control: post-check=0, pre-check=0", false);
|
||||
header("Pragma: no-cache"); // HTTP/1.0
|
||||
|
||||
function duration($ts) {
|
||||
global $time;
|
||||
$years = (int)((($time - $ts)/(7*86400))/52.177457);
|
||||
$rem = (int)(($time-$ts)-($years * 52.177457 * 7 * 86400));
|
||||
$weeks = (int)(($rem)/(7*86400));
|
||||
$days = (int)(($rem)/86400) - $weeks*7;
|
||||
$hours = (int)(($rem)/3600) - $days*24 - $weeks*7*24;
|
||||
$mins = (int)(($rem)/60) - $hours*60 - $days*24*60 - $weeks*7*24*60;
|
||||
$str = '';
|
||||
if($years==1) $str .= "$years year, ";
|
||||
if($years>1) $str .= "$years years, ";
|
||||
if($weeks==1) $str .= "$weeks week, ";
|
||||
if($weeks>1) $str .= "$weeks weeks, ";
|
||||
if($days==1) $str .= "$days day,";
|
||||
if($days>1) $str .= "$days days,";
|
||||
if($hours == 1) $str .= " $hours hour and";
|
||||
if($hours>1) $str .= " $hours hours and";
|
||||
if($mins == 1) $str .= " 1 minute";
|
||||
else $str .= " $mins minutes";
|
||||
return $str;
|
||||
}
|
||||
|
||||
// create graphics
|
||||
//
|
||||
function graphics_avail() {
|
||||
return extension_loaded('gd');
|
||||
}
|
||||
|
||||
function bsize($s) {
|
||||
foreach (array('','K','M','G') as $i => $k) {
|
||||
if ($s < 1024) break;
|
||||
$s/=1024;
|
||||
}
|
||||
return sprintf("%5.1f %sBytes",$s,$k);
|
||||
}
|
||||
|
||||
// create menu entry
|
||||
function menu_entry($ob,$title) {
|
||||
global $PHP_SELF;
|
||||
if ($ob==$_GET['op']){
|
||||
return "<li><a class=\"child_active\" href=\"$PHP_SELF&op=$ob\">$title</a></li>";
|
||||
}
|
||||
return "<li><a class=\"active\" href=\"$PHP_SELF&op=$ob\">$title</a></li>";
|
||||
}
|
||||
|
||||
function getHeader(){
|
||||
$header = <<<EOB
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head><title>MEMCACHE INFO</title>
|
||||
<style type="text/css"><!--
|
||||
body { background:white; font-size:100.01%; margin:0; padding:0; }
|
||||
body,p,td,th,input,submit { font-size:0.8em;font-family:arial,helvetica,sans-serif; }
|
||||
* html body {font-size:0.8em}
|
||||
* html p {font-size:0.8em}
|
||||
* html td {font-size:0.8em}
|
||||
* html th {font-size:0.8em}
|
||||
* html input {font-size:0.8em}
|
||||
* html submit {font-size:0.8em}
|
||||
td { vertical-align:top }
|
||||
a { color:black; font-weight:none; text-decoration:none; }
|
||||
a:hover { text-decoration:underline; }
|
||||
div.content { padding:1em 1em 1em 1em; position:absolute; width:97%; z-index:100; }
|
||||
|
||||
h1.memcache { background:rgb(153,153,204); margin:0; padding:0.5em 1em 0.5em 1em; }
|
||||
* html h1.memcache { margin-bottom:-7px; }
|
||||
h1.memcache a:hover { text-decoration:none; color:rgb(90,90,90); }
|
||||
h1.memcache span.logo {
|
||||
background:rgb(119,123,180);
|
||||
color:black;
|
||||
border-right: solid black 1px;
|
||||
border-bottom: solid black 1px;
|
||||
font-style:italic;
|
||||
font-size:1em;
|
||||
padding-left:1.2em;
|
||||
padding-right:1.2em;
|
||||
text-align:right;
|
||||
display:block;
|
||||
width:130px;
|
||||
}
|
||||
h1.memcache span.logo span.name { color:white; font-size:0.7em; padding:0 0.8em 0 2em; }
|
||||
h1.memcache span.nameinfo { color:white; display:inline; font-size:0.4em; margin-left: 3em; }
|
||||
h1.memcache div.copy { color:black; font-size:0.4em; position:absolute; right:1em; }
|
||||
hr.memcache {
|
||||
background:white;
|
||||
border-bottom:solid rgb(102,102,153) 1px;
|
||||
border-style:none;
|
||||
border-top:solid rgb(102,102,153) 10px;
|
||||
height:12px;
|
||||
margin:0;
|
||||
margin-top:1px;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
ol,menu { margin:1em 0 0 0; padding:0.2em; margin-left:1em;}
|
||||
ol.menu li { display:inline; margin-right:0.7em; list-style:none; font-size:85%}
|
||||
ol.menu a {
|
||||
background:rgb(153,153,204);
|
||||
border:solid rgb(102,102,153) 2px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
margin-right:0em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
text-decoration:none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
ol.menu a.child_active {
|
||||
background:rgb(153,153,204);
|
||||
border:solid rgb(102,102,153) 2px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
margin-right:0em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
text-decoration:none;
|
||||
border-left: solid black 5px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
ol.menu span.active {
|
||||
background:rgb(153,153,204);
|
||||
border:solid rgb(102,102,153) 2px;
|
||||
color:black;
|
||||
font-weight:bold;
|
||||
margin-right:0em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
text-decoration:none;
|
||||
border-left: solid black 5px;
|
||||
}
|
||||
ol.menu span.inactive {
|
||||
background:rgb(193,193,244);
|
||||
border:solid rgb(182,182,233) 2px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
margin-right:0em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
text-decoration:none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
ol.menu a:hover {
|
||||
background:rgb(193,193,244);
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
|
||||
div.info {
|
||||
background:rgb(204,204,204);
|
||||
border:solid rgb(204,204,204) 1px;
|
||||
margin-bottom:1em;
|
||||
}
|
||||
div.info h2 {
|
||||
background:rgb(204,204,204);
|
||||
color:black;
|
||||
font-size:1em;
|
||||
margin:0;
|
||||
padding:0.1em 1em 0.1em 1em;
|
||||
}
|
||||
div.info table {
|
||||
border:solid rgb(204,204,204) 1px;
|
||||
border-spacing:0;
|
||||
width:100%;
|
||||
}
|
||||
div.info table th {
|
||||
background:rgb(204,204,204);
|
||||
color:white;
|
||||
margin:0;
|
||||
padding:0.1em 1em 0.1em 1em;
|
||||
}
|
||||
div.info table th a.sortable { color:black; }
|
||||
div.info table tr.tr-0 { background:rgb(238,238,238); }
|
||||
div.info table tr.tr-1 { background:rgb(221,221,221); }
|
||||
div.info table td { padding:0.3em 1em 0.3em 1em; }
|
||||
div.info table td.td-0 { border-right:solid rgb(102,102,153) 1px; white-space:nowrap; }
|
||||
div.info table td.td-n { border-right:solid rgb(102,102,153) 1px; }
|
||||
div.info table td h3 {
|
||||
color:black;
|
||||
font-size:1.1em;
|
||||
margin-left:-0.3em;
|
||||
}
|
||||
.td-0 a , .td-n a, .tr-0 a , tr-1 a {
|
||||
text-decoration:underline;
|
||||
}
|
||||
div.graph { margin-bottom:1em }
|
||||
div.graph h2 { background:rgb(204,204,204);; color:black; font-size:1em; margin:0; padding:0.1em 1em 0.1em 1em; }
|
||||
div.graph table { border:solid rgb(204,204,204) 1px; color:black; font-weight:normal; width:100%; }
|
||||
div.graph table td.td-0 { background:rgb(238,238,238); }
|
||||
div.graph table td.td-1 { background:rgb(221,221,221); }
|
||||
div.graph table td { padding:0.2em 1em 0.4em 1em; }
|
||||
|
||||
div.div1,div.div2 { margin-bottom:1em; width:35em; }
|
||||
div.div3 { position:absolute; left:40em; top:1em; width:580px; }
|
||||
//div.div3 { position:absolute; left:37em; top:1em; right:1em; }
|
||||
|
||||
div.sorting { margin:1.5em 0em 1.5em 2em }
|
||||
.center { text-align:center }
|
||||
.aright { position:absolute;right:1em }
|
||||
.right { text-align:right }
|
||||
.ok { color:rgb(0,200,0); font-weight:bold}
|
||||
.failed { color:rgb(200,0,0); font-weight:bold}
|
||||
|
||||
span.box {
|
||||
border: black solid 1px;
|
||||
border-right:solid black 2px;
|
||||
border-bottom:solid black 2px;
|
||||
padding:0 0.5em 0 0.5em;
|
||||
margin-right:1em;
|
||||
}
|
||||
span.green { background:#60F060; padding:0 0.5em 0 0.5em}
|
||||
span.red { background:#D06030; padding:0 0.5em 0 0.5em }
|
||||
|
||||
div.authneeded {
|
||||
background:rgb(238,238,238);
|
||||
border:solid rgb(204,204,204) 1px;
|
||||
color:rgb(200,0,0);
|
||||
font-size:1.2em;
|
||||
font-weight:bold;
|
||||
padding:2em;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
input {
|
||||
background:rgb(153,153,204);
|
||||
border:solid rgb(102,102,153) 2px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
margin-right:1em;
|
||||
padding:0.1em 0.5em 0.1em 0.5em;
|
||||
}
|
||||
//-->
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="head">
|
||||
<h1 class="memcache">
|
||||
<span class="logo"><a href="http://pecl.php.net/package/memcache">memcache</a></span>
|
||||
<span class="nameinfo">memcache.php by <a href="http://livebookmark.net">Harun Yayli</a></span>
|
||||
</h1>
|
||||
<hr class="memcache">
|
||||
</div>
|
||||
<div class=content>
|
||||
EOB;
|
||||
|
||||
return $header;
|
||||
}
|
||||
function getFooter(){
|
||||
global $VERSION;
|
||||
$footer = '</div><!-- Based on apc.php '.$VERSION.'--></body>
|
||||
</html>
|
||||
';
|
||||
|
||||
return $footer;
|
||||
|
||||
}
|
||||
function getMenu(){
|
||||
global $PHP_SELF;
|
||||
echo "<ol class=menu>";
|
||||
if ($_GET['op']!=4){
|
||||
echo <<<EOB
|
||||
<li><a href="$PHP_SELF&op={$_GET['op']}">Refresh Data</a></li>
|
||||
EOB;
|
||||
}
|
||||
else {
|
||||
echo <<<EOB
|
||||
<li><a href="$PHP_SELF&op=2}">Back</a></li>
|
||||
EOB;
|
||||
}
|
||||
echo
|
||||
menu_entry(1,'View Host Stats'),
|
||||
menu_entry(2,'Variables');
|
||||
|
||||
echo <<<EOB
|
||||
</ol>
|
||||
<br/>
|
||||
EOB;
|
||||
}
|
||||
|
||||
// TODO, AUTH
|
||||
|
||||
$_GET['op'] = !isset($_GET['op'])? '1':$_GET['op'];
|
||||
$PHP_SELF= isset($_SERVER['PHP_SELF']) ? htmlentities(strip_tags($_SERVER['PHP_SELF'],'')) : '';
|
||||
|
||||
$PHP_SELF=$PHP_SELF.'?';
|
||||
$time = time();
|
||||
// sanitize _GET
|
||||
|
||||
foreach($_GET as $key=>$g){
|
||||
$_GET[$key]=htmlentities($g);
|
||||
}
|
||||
|
||||
|
||||
// singleout
|
||||
// when singleout is set, it only gives details for that server.
|
||||
if (isset($_GET['singleout']) && $_GET['singleout']>=0 && $_GET['singleout'] <count($MEMCACHE_SERVERS)){
|
||||
$MEMCACHE_SERVERS = array($MEMCACHE_SERVERS[$_GET['singleout']]);
|
||||
}
|
||||
|
||||
// display images
|
||||
if (isset($_GET['IMG'])){
|
||||
$memcacheStats = getMemcacheStats();
|
||||
$memcacheStatsSingle = getMemcacheStats(false);
|
||||
|
||||
if (!graphics_avail()) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function fill_box($im, $x, $y, $w, $h, $color1, $color2,$text='',$placeindex='') {
|
||||
global $col_black;
|
||||
$x1=$x+$w-1;
|
||||
$y1=$y+$h-1;
|
||||
|
||||
imagerectangle($im, $x, $y1, $x1+1, $y+1, $col_black);
|
||||
if($y1>$y) imagefilledrectangle($im, $x, $y, $x1, $y1, $color2);
|
||||
else imagefilledrectangle($im, $x, $y1, $x1, $y, $color2);
|
||||
imagerectangle($im, $x, $y1, $x1, $y, $color1);
|
||||
if ($text) {
|
||||
if ($placeindex>0) {
|
||||
|
||||
if ($placeindex<16)
|
||||
{
|
||||
$px=5;
|
||||
$py=$placeindex*12+6;
|
||||
imagefilledrectangle($im, $px+90, $py+3, $px+90-4, $py-3, $color2);
|
||||
imageline($im,$x,$y+$h/2,$px+90,$py,$color2);
|
||||
imagestring($im,2,$px,$py-6,$text,$color1);
|
||||
|
||||
} else {
|
||||
if ($placeindex<31) {
|
||||
$px=$x+40*2;
|
||||
$py=($placeindex-15)*12+6;
|
||||
} else {
|
||||
$px=$x+40*2+100*intval(($placeindex-15)/15);
|
||||
$py=($placeindex%15)*12+6;
|
||||
}
|
||||
imagefilledrectangle($im, $px, $py+3, $px-4, $py-3, $color2);
|
||||
imageline($im,$x+$w,$y+$h/2,$px,$py,$color2);
|
||||
imagestring($im,2,$px+2,$py-6,$text,$color1);
|
||||
}
|
||||
} else {
|
||||
imagestring($im,4,$x+5,$y1-16,$text,$color1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function fill_arc($im, $centerX, $centerY, $diameter, $start, $end, $color1,$color2,$text='',$placeindex=0) {
|
||||
$r=$diameter/2;
|
||||
$w=deg2rad((360+$start+($end-$start)/2)%360);
|
||||
|
||||
|
||||
if (function_exists("imagefilledarc")) {
|
||||
// exists only if GD 2.0.1 is avaliable
|
||||
imagefilledarc($im, $centerX+1, $centerY+1, $diameter, $diameter, $start, $end, $color1, IMG_ARC_PIE);
|
||||
imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2, IMG_ARC_PIE);
|
||||
imagefilledarc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color1, IMG_ARC_NOFILL|IMG_ARC_EDGED);
|
||||
} else {
|
||||
imagearc($im, $centerX, $centerY, $diameter, $diameter, $start, $end, $color2);
|
||||
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
|
||||
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($start+1)) * $r, $centerY + sin(deg2rad($start)) * $r, $color2);
|
||||
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end-1)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
|
||||
imageline($im, $centerX, $centerY, $centerX + cos(deg2rad($end)) * $r, $centerY + sin(deg2rad($end)) * $r, $color2);
|
||||
imagefill($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2, $color2);
|
||||
}
|
||||
if ($text) {
|
||||
if ($placeindex>0) {
|
||||
imageline($im,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$diameter, $placeindex*12,$color1);
|
||||
imagestring($im,4,$diameter, $placeindex*12,$text,$color1);
|
||||
|
||||
} else {
|
||||
imagestring($im,4,$centerX + $r*cos($w)/2, $centerY + $r*sin($w)/2,$text,$color1);
|
||||
}
|
||||
}
|
||||
}
|
||||
$size = GRAPH_SIZE; // image size
|
||||
$image = imagecreate($size+50, $size+10);
|
||||
|
||||
$col_white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
|
||||
$col_red = imagecolorallocate($image, 0xD0, 0x60, 0x30);
|
||||
$col_green = imagecolorallocate($image, 0x60, 0xF0, 0x60);
|
||||
$col_black = imagecolorallocate($image, 0, 0, 0);
|
||||
|
||||
imagecolortransparent($image,$col_white);
|
||||
|
||||
switch ($_GET['IMG']){
|
||||
case 1: // pie chart
|
||||
$tsize=$memcacheStats['limit_maxbytes'];
|
||||
$avail=$tsize-$memcacheStats['bytes'];
|
||||
$x=$y=$size/2;
|
||||
$angle_from = 0;
|
||||
$fuzz = 0.000001;
|
||||
|
||||
foreach($memcacheStatsSingle as $serv=>$mcs) {
|
||||
$free = $mcs['STAT']['limit_maxbytes']-$mcs['STAT']['bytes'];
|
||||
$used = $mcs['STAT']['bytes'];
|
||||
|
||||
|
||||
if ($free>0){
|
||||
// draw free
|
||||
$angle_to = ($free*360)/$tsize;
|
||||
$perc =sprintf("%.2f%%", ($free *100) / $tsize) ;
|
||||
|
||||
fill_arc($image,$x,$y,$size,$angle_from,$angle_from + $angle_to ,$col_black,$col_green,$perc);
|
||||
$angle_from = $angle_from + $angle_to ;
|
||||
}
|
||||
if ($used>0){
|
||||
// draw used
|
||||
$angle_to = ($used*360)/$tsize;
|
||||
$perc =sprintf("%.2f%%", ($used *100) / $tsize) ;
|
||||
fill_arc($image,$x,$y,$size,$angle_from,$angle_from + $angle_to ,$col_black,$col_red, '('.$perc.')' );
|
||||
$angle_from = $angle_from+ $angle_to ;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2: // hit miss
|
||||
|
||||
$hits = ($memcacheStats['get_hits']==0) ? 1:$memcacheStats['get_hits'];
|
||||
$misses = ($memcacheStats['get_misses']==0) ? 1:$memcacheStats['get_misses'];
|
||||
$total = $hits + $misses ;
|
||||
|
||||
fill_box($image, 30,$size,50,-$hits*($size-21)/$total,$col_black,$col_green,sprintf("%.1f%%",$hits*100/$total));
|
||||
fill_box($image,130,$size,50,-max(4,($total-$hits)*($size-21)/$total),$col_black,$col_red,sprintf("%.1f%%",$misses*100/$total));
|
||||
break;
|
||||
|
||||
}
|
||||
header("Content-type: image/png");
|
||||
imagepng($image);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo getHeader();
|
||||
echo getMenu();
|
||||
|
||||
switch ($_GET['op']) {
|
||||
|
||||
case 1: // host stats
|
||||
$phpversion = phpversion();
|
||||
$memcacheStats = getMemcacheStats();
|
||||
$memcacheStatsSingle = getMemcacheStats(false);
|
||||
|
||||
$mem_size = $memcacheStats['limit_maxbytes'];
|
||||
$mem_used = $memcacheStats['bytes'];
|
||||
$mem_avail= $mem_size-$mem_used;
|
||||
$startTime = time()-array_sum($memcacheStats['uptime']);
|
||||
|
||||
$curr_items = $memcacheStats['curr_items'];
|
||||
$total_items = $memcacheStats['total_items'];
|
||||
$hits = ($memcacheStats['get_hits']==0) ? 1:$memcacheStats['get_hits'];
|
||||
$misses = ($memcacheStats['get_misses']==0) ? 1:$memcacheStats['get_misses'];
|
||||
$sets = $memcacheStats['cmd_set'];
|
||||
|
||||
$req_rate = sprintf("%.2f",($hits+$misses)/($time-$startTime));
|
||||
$hit_rate = sprintf("%.2f",($hits)/($time-$startTime));
|
||||
$miss_rate = sprintf("%.2f",($misses)/($time-$startTime));
|
||||
$set_rate = sprintf("%.2f",($sets)/($time-$startTime));
|
||||
|
||||
echo <<< EOB
|
||||
<div class="info div1"><h2>General Cache Information</h2>
|
||||
<table cellspacing=0><tbody>
|
||||
<tr class=tr-1><td class=td-0>PHP Version</td><td>$phpversion</td></tr>
|
||||
EOB;
|
||||
echo "<tr class=tr-0><td class=td-0>Memcached Host". ((count($MEMCACHE_SERVERS)>1) ? 's':'')."</td><td>";
|
||||
$i=0;
|
||||
if (!isset($_GET['singleout']) && count($MEMCACHE_SERVERS)>1){
|
||||
foreach($MEMCACHE_SERVERS as $server){
|
||||
echo ($i+1).'. <a href="'.$PHP_SELF.'&singleout='.$i++.'">'.$server.'</a><br/>';
|
||||
}
|
||||
}
|
||||
else{
|
||||
echo '1.'.$MEMCACHE_SERVERS[0];
|
||||
}
|
||||
if (isset($_GET['singleout'])){
|
||||
echo '<a href="'.$PHP_SELF.'">(all servers)</a><br/>';
|
||||
}
|
||||
echo "</td></tr>\n";
|
||||
echo "<tr class=tr-1><td class=td-0>Total Memcache Cache</td><td>".bsize($memcacheStats['limit_maxbytes'])."</td></tr>\n";
|
||||
|
||||
echo <<<EOB
|
||||
</tbody></table>
|
||||
</div>
|
||||
|
||||
<div class="info div1"><h2>Memcache Server Information</h2>
|
||||
EOB;
|
||||
foreach($MEMCACHE_SERVERS as $server){
|
||||
echo '<table cellspacing=0><tbody>';
|
||||
echo '<tr class=tr-1><td class=td-1>'.$server.'</td><td><a href="'.$PHP_SELF.'&server='.array_search($server,$MEMCACHE_SERVERS).'&op=6">[<b>Flush this server</b>]</a></td></tr>';
|
||||
echo '<tr class=tr-0><td class=td-0>Start Time</td><td>',date(DATE_FORMAT,$memcacheStatsSingle[$server]['STAT']['time']-$memcacheStatsSingle[$server]['STAT']['uptime']),'</td></tr>';
|
||||
echo '<tr class=tr-1><td class=td-0>Uptime</td><td>',duration($memcacheStatsSingle[$server]['STAT']['time']-$memcacheStatsSingle[$server]['STAT']['uptime']),'</td></tr>';
|
||||
echo '<tr class=tr-0><td class=td-0>Memcached Server Version</td><td>'.$memcacheStatsSingle[$server]['STAT']['version'].'</td></tr>';
|
||||
echo '<tr class=tr-1><td class=td-0>Used Cache Size</td><td>',bsize($memcacheStatsSingle[$server]['STAT']['bytes']),'</td></tr>';
|
||||
echo '<tr class=tr-0><td class=td-0>Total Cache Size</td><td>',bsize($memcacheStatsSingle[$server]['STAT']['limit_maxbytes']),'</td></tr>';
|
||||
echo '</tbody></table>';
|
||||
}
|
||||
echo <<<EOB
|
||||
|
||||
</div>
|
||||
<div class="graph div3"><h2>Host Status Diagrams</h2>
|
||||
<table cellspacing=0><tbody>
|
||||
EOB;
|
||||
|
||||
$size='width='.(GRAPH_SIZE+50).' height='.(GRAPH_SIZE+10);
|
||||
echo <<<EOB
|
||||
<tr>
|
||||
<td class=td-0>Cache Usage</td>
|
||||
<td class=td-1>Hits & Misses</td>
|
||||
</tr>
|
||||
EOB;
|
||||
|
||||
echo
|
||||
graphics_avail() ?
|
||||
'<tr>'.
|
||||
"<td class=td-0><img alt=\"\" $size src=\"$PHP_SELF&IMG=1&".(isset($_GET['singleout'])? 'singleout='.$_GET['singleout'].'&':'')."$time\"></td>".
|
||||
"<td class=td-1><img alt=\"\" $size src=\"$PHP_SELF&IMG=2&".(isset($_GET['singleout'])? 'singleout='.$_GET['singleout'].'&':'')."$time\"></td></tr>\n"
|
||||
: "",
|
||||
'<tr>',
|
||||
'<td class=td-0><span class="green box"> </span>Free: ',bsize($mem_avail).sprintf(" (%.1f%%)",$mem_avail*100/$mem_size),"</td>\n",
|
||||
'<td class=td-1><span class="green box"> </span>Hits: ',$hits.sprintf(" (%.1f%%)",$hits*100/($hits+$misses)),"</td>\n",
|
||||
'</tr>',
|
||||
'<tr>',
|
||||
'<td class=td-0><span class="red box"> </span>Used: ',bsize($mem_used ).sprintf(" (%.1f%%)",$mem_used *100/$mem_size),"</td>\n",
|
||||
'<td class=td-1><span class="red box"> </span>Misses: ',$misses.sprintf(" (%.1f%%)",$misses*100/($hits+$misses)),"</td>\n";
|
||||
echo <<< EOB
|
||||
</tr>
|
||||
</tbody></table>
|
||||
<br/>
|
||||
<div class="info"><h2>Cache Information</h2>
|
||||
<table cellspacing=0><tbody>
|
||||
<tr class=tr-0><td class=td-0>Current Items(total)</td><td>$curr_items ($total_items)</td></tr>
|
||||
<tr class=tr-1><td class=td-0>Hits</td><td>{$hits}</td></tr>
|
||||
<tr class=tr-0><td class=td-0>Misses</td><td>{$misses}</td></tr>
|
||||
<tr class=tr-1><td class=td-0>Request Rate (hits, misses)</td><td>$req_rate cache requests/second</td></tr>
|
||||
<tr class=tr-0><td class=td-0>Hit Rate</td><td>$hit_rate cache requests/second</td></tr>
|
||||
<tr class=tr-1><td class=td-0>Miss Rate</td><td>$miss_rate cache requests/second</td></tr>
|
||||
<tr class=tr-0><td class=td-0>Set Rate</td><td>$set_rate cache requests/second</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
|
||||
EOB;
|
||||
|
||||
break;
|
||||
|
||||
case 2: // variables
|
||||
|
||||
$m=0;
|
||||
$cacheItems= getCacheItems();
|
||||
$items = $cacheItems['items'];
|
||||
$totals = $cacheItems['counts'];
|
||||
$maxDump = MAX_ITEM_DUMP;
|
||||
foreach($items as $server => $entries) {
|
||||
|
||||
echo <<< EOB
|
||||
|
||||
<div class="info"><table cellspacing=0><tbody>
|
||||
<tr><th colspan="2">$server</th></tr>
|
||||
<tr><th>Slab Id</th><th>Info</th></tr>
|
||||
EOB;
|
||||
|
||||
foreach($entries as $slabId => $slab) {
|
||||
$dumpUrl = $PHP_SELF.'&op=2&server='.(array_search($server,$MEMCACHE_SERVERS)).'&dumpslab='.$slabId;
|
||||
echo
|
||||
"<tr class=tr-$m>",
|
||||
"<td class=td-0><center>",'<a href="',$dumpUrl,'">',$slabId,'</a>',"</center></td>",
|
||||
"<td class=td-last><b>Item count:</b> ",$slab['number'],'<br/><b>Age:</b>',duration($time-$slab['age']),'<br/> <b>Evicted:</b>',((isset($slab['evicted']) && $slab['evicted']==1)? 'Yes':'No');
|
||||
if ((isset($_GET['dumpslab']) && $_GET['dumpslab']==$slabId) && (isset($_GET['server']) && $_GET['server']==array_search($server,$MEMCACHE_SERVERS))){
|
||||
echo "<br/><b>Items: item</b><br/>";
|
||||
$items = dumpCacheSlab($server,$slabId,$slab['number']);
|
||||
// maybe someone likes to do a pagination here :)
|
||||
$i=1;
|
||||
foreach($items['ITEM'] as $itemKey=>$itemInfo){
|
||||
$itemInfo = trim($itemInfo,'[ ]');
|
||||
|
||||
|
||||
echo '<a href="',$PHP_SELF,'&op=4&server=',(array_search($server,$MEMCACHE_SERVERS)),'&key=',base64_encode($itemKey).'">',$itemKey,'</a>';
|
||||
if ($i++ % 10 == 0) {
|
||||
echo '<br/>';
|
||||
}
|
||||
elseif ($i!=$slab['number']+1){
|
||||
echo ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "</td></tr>";
|
||||
$m=1-$m;
|
||||
}
|
||||
echo <<<EOB
|
||||
</tbody></table>
|
||||
</div><hr/>
|
||||
EOB;
|
||||
}
|
||||
break;
|
||||
|
||||
break;
|
||||
|
||||
case 4: //item dump
|
||||
if (!isset($_GET['key']) || !isset($_GET['server'])){
|
||||
echo "No key set!";
|
||||
break;
|
||||
}
|
||||
// I'm not doing anything to check the validity of the key string.
|
||||
// probably an exploit can be written to delete all the files in key=base64_encode("\n\r delete all").
|
||||
// somebody has to do a fix to this.
|
||||
$theKey = htmlentities(base64_decode($_GET['key']));
|
||||
|
||||
$theserver = $MEMCACHE_SERVERS[(int)$_GET['server']];
|
||||
list($h,$p) = get_host_port_from_server($theserver);
|
||||
$r = sendMemcacheCommand($h,$p,'get '.$theKey);
|
||||
echo <<<EOB
|
||||
<div class="info"><table cellspacing=0><tbody>
|
||||
<tr><th>Server<th>Key</th><th>Value</th><th>Delete</th></tr>
|
||||
EOB;
|
||||
if (!isset($r['VALUE'])) {
|
||||
echo "<tr><td class=td-0>",$theserver,"</td><td class=td-0>",$theKey,
|
||||
"</td><td>[The requested item was not found or has expired]</td>",
|
||||
"<td></td>","</tr>";
|
||||
}
|
||||
else {
|
||||
|
||||
echo "<tr><td class=td-0>",$theserver,"</td><td class=td-0>",$theKey,
|
||||
" <br/>flag:",$r['VALUE'][$theKey]['stat']['flag'],
|
||||
" <br/>Size:",bsize($r['VALUE'][$theKey]['stat']['size']),
|
||||
"</td><td>",chunk_split($r['VALUE'][$theKey]['value'],40),"</td>",
|
||||
'<td><a href="',$PHP_SELF,'&op=5&server=',(int)$_GET['server'],'&key=',base64_encode($theKey),"\">Delete</a></td>","</tr>";
|
||||
}
|
||||
echo <<<EOB
|
||||
</tbody></table>
|
||||
</div><hr/>
|
||||
EOB;
|
||||
break;
|
||||
case 5: // item delete
|
||||
if (!isset($_GET['key']) || !isset($_GET['server'])){
|
||||
echo "No key set!";
|
||||
break;
|
||||
}
|
||||
$theKey = htmlentities(base64_decode($_GET['key']));
|
||||
$theserver = $MEMCACHE_SERVERS[(int)$_GET['server']];
|
||||
list($h,$p) = get_host_port_from_server($theserver);
|
||||
$r = sendMemcacheCommand($h,$p,'delete '.$theKey);
|
||||
echo 'Deleting '.$theKey.':'.$r;
|
||||
break;
|
||||
|
||||
case 6: // flush server
|
||||
$_GET['server'] = empty($_GET['server']) ? 0 : $_GET['server'];
|
||||
$theserver = $MEMCACHE_SERVERS[(int)$_GET['server']];
|
||||
$r = flushServer($theserver);
|
||||
echo 'Flush '.$theserver.":".$r;
|
||||
break;
|
||||
}
|
||||
echo getFooter();
|
||||
|
||||
?>
|
||||
Binary file not shown.
BIN
laragon.exe
BIN
laragon.exe
Binary file not shown.
@@ -12,4 +12,3 @@
|
||||
; My Cool App: autorun node start $PORT PORT=9000 env_file=usr\my_file.env PWD=usr/proj/myapp DB_HOST=1.2.3.4 DB_PORT=9999 DB_USER=user DB_PASS="!$Ab.cs3cre1" DB_NAME=cooldb
|
||||
; Awesome Tool: "PATH HAS SPACES SHOULD BE QUOTED LIKE THIS"
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -12,4 +12,3 @@
|
||||
; My Cool App: autorun node start $PORT PORT=9000 env_file=usr\my_file.env PWD=usr/proj/myapp DB_HOST=1.2.3.4 DB_PORT=9999 DB_USER=user DB_PASS="!$Ab.cs3cre1" DB_NAME=cooldb
|
||||
; Awesome Tool: "PATH HAS SPACES SHOULD BE QUOTED LIKE THIS"
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
# PHP
|
||||
# https://windows.php.net/download/
|
||||
# NTS = Non Thread Safe
|
||||
*PHP-8.4=https://windows.php.net/downloads/releases/archives/php-8.4.2-nts-Win32-vs17-x64.zip
|
||||
*PHP-8.3=https://windows.php.net/downloads/releases/archives/php-8.3.15-nts-Win32-vs16-x64.zip
|
||||
*PHP-8.2=https://windows.php.net/downloads/releases/archives/php-8.2.27-nts-Win32-vs16-x64.zip
|
||||
*PHP-8.1=https://windows.php.net/downloads/releases/archives/php-8.1.31-nts-Win32-vs16-x64.zip
|
||||
*PHP-8.4=https://windows.php.net/downloads/releases/archives/php-8.4.3-nts-Win32-vs17-x64.zip
|
||||
*PHP-8.3=https://windows.php.net/downloads/releases/archives/php-8.3.16-nts-Win32-vs16-x64.zip
|
||||
*PHP-8.2=https://windows.php.net/downloads/releases/archives/php-8.2.26-nts-Win32-vs16-x64.zip
|
||||
*PHP-8.1=https://windows.php.net/downloads/releases/archives/php-8.1.30-nts-Win32-vs16-x64.zip
|
||||
|
||||
|
||||
---
|
||||
# Web Servers
|
||||
Apache-2.4.63=https://www.apachelounge.com/download/VS17/binaries/httpd-2.4.63-250122-win64-VS17.zip
|
||||
Apache-2.4.57=https://www.apachelounge.com/download/VS16/binaries/httpd-2.4.57-win64-VS16.zip
|
||||
Nginx-1.27.4=https://nginx.org/download/nginx-1.27.4.zip
|
||||
|
||||
---
|
||||
|
||||
|
||||
# Node.js
|
||||
# https://nodejs.org/en/download/prebuilt-binaries/current
|
||||
node-23=https://nodejs.org/dist/v23.4.0/node-v23.4.0-win-x64.zip
|
||||
node-22=https://nodejs.org/dist/v22.12.0/node-v22.12.0-win-x64.zip
|
||||
node-23.9=https://nodejs.org/dist/v23.9.0/node-v23.9.0-win-x64.zip
|
||||
node-22.14=https://nodejs.org/dist/v22.14.0/node-v22.14.0-win-x64.zip
|
||||
|
||||
---
|
||||
|
||||
|
||||
# phpMyAdmin
|
||||
# After download, visit -> http://localhost/phpmyadmin
|
||||
phpmyadmin=https://files.phpmyadmin.net/phpMyAdmin/5.2.1/phpMyAdmin-5.2.1-all-languages.zip
|
||||
phpmyadmin-6.0snapshot=https://files.phpmyadmin.net/snapshots/phpMyAdmin-6.0+snapshot-english.tar.xz
|
||||
phpmyadmin=https://files.phpmyadmin.net/phpMyAdmin/5.2.2/phpMyAdmin-5.2.2-english.zip
|
||||
|
||||
# DB Tools
|
||||
DBeaver=https://dbeaver.io/files/dbeaver-ce-latest-win32.win32.x86_64.zip
|
||||
@@ -56,8 +64,10 @@ code=https://go.microsoft.com/fwlink/?Linkid=850641
|
||||
---
|
||||
# Golang
|
||||
# https://go.dev/dl/
|
||||
golang-1.23=https://go.dev/dl/go1.23.4.windows-amd64.zip
|
||||
go-1.24=https://go.dev/dl/go1.24.1.windows-amd64.zip
|
||||
go-1.23=https://go.dev/dl/go1.23.4.windows-amd64.zip
|
||||
|
||||
|
||||
# Pocketbase
|
||||
# https://github.com/pocketbase/pocketbase/releases
|
||||
pocketbase=https://github.com/pocketbase/pocketbase/releases/download/v0.23.7/pocketbase_0.23.7_windows_amd64.zip
|
||||
pocketbase=https://github.com/pocketbase/pocketbase/releases/download/v0.25.9/pocketbase_0.25.9_windows_amd64.zip
|
||||
|
||||
@@ -2,5 +2,32 @@
|
||||
AutoCreateDatabase=true
|
||||
Cached=true
|
||||
|
||||
# Blank: an empty project
|
||||
Blank=
|
||||
|
||||
# WordPress
|
||||
WordPress=https://wordpress.org/latest.zip
|
||||
WordPress=https://wordpress.org/latest.tar.gz
|
||||
------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Laravel
|
||||
|
||||
Laravel=composer create-project laravel/laravel %s --prefer-dist
|
||||
Laravel CLI=laravel new %s
|
||||
# Laravel (zip)=PATH-TO-YOUR-ZIP
|
||||
------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# CakePHP
|
||||
### CakePHP=composer create-project --prefer-dist cakephp/app %s
|
||||
|
||||
# Symfony
|
||||
Symfony=composer create-project symfony/website-skeleton %s
|
||||
|
||||
232
www/index.php
232
www/index.php
@@ -1,167 +1,105 @@
|
||||
<?php
|
||||
if (!empty($_GET['q'])) {
|
||||
switch ($_GET['q']) {
|
||||
case 'info':
|
||||
phpinfo();
|
||||
exit;
|
||||
break;
|
||||
}
|
||||
$query = htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8');
|
||||
|
||||
switch ($query) {
|
||||
case 'info':
|
||||
phpinfo();
|
||||
exit;
|
||||
default:
|
||||
header("HTTP/1.0 404 Not Found");
|
||||
echo "Invalid query parameter.";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Laragon</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Karla:400" rel="stylesheet" type="text/css">
|
||||
<link rel="shortcut icon" href="https://i.imgur.com/ky9oqct.png" type="image/png">
|
||||
<style>
|
||||
*,
|
||||
:before *,
|
||||
:after * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Laragon</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Karla:400" rel="stylesheet" type="text/css">
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Karla', sans-serif;
|
||||
font-weight: 100;
|
||||
background-color: #f9f9f9;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
font-weight: 100;
|
||||
font-family: 'Karla', sans-serif;
|
||||
font-size: 18px;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header,
|
||||
main,
|
||||
nav,
|
||||
aside {
|
||||
padding: 1rem;
|
||||
margin: auto;
|
||||
max-width: 1200px;
|
||||
text-align: center;
|
||||
}
|
||||
.content {
|
||||
max-width: 800px;
|
||||
padding: 100px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.title {
|
||||
font-size: 60px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header__item {
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
.info {
|
||||
margin-top: 20px;
|
||||
font-size: 18px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.header--logo {
|
||||
height: 8rem;
|
||||
}
|
||||
.info a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 5rem;
|
||||
}
|
||||
.info a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
main {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.opt {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
nav {
|
||||
width: 100%;
|
||||
}
|
||||
.opt a {
|
||||
font-size: 18px;
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #37ADFF;
|
||||
font-weight: 900;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: red;
|
||||
font-weight: 900;
|
||||
transition: 300ms;
|
||||
}
|
||||
|
||||
main a {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: block;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
nav a:after {
|
||||
content: '→';
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.alert {
|
||||
color: red;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
@media (min-width: 650px) {
|
||||
h1 {
|
||||
font-size: 10rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
.opt a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<img class="header__item header--logo" src="https://i.imgur.com/ky9oqct.png" alt="Offline">
|
||||
<h1 class="header__item header--title" title="Laragon">Laragon</h1>
|
||||
</header>
|
||||
<main>
|
||||
<p>
|
||||
<?php print($_SERVER['SERVER_SOFTWARE']); ?>
|
||||
</p>
|
||||
<p>
|
||||
PHP version: <?php print PHP_VERSION; ?> <span><a title="phpinfo()" href="/?q=info">info</a></span>
|
||||
</p>
|
||||
<p>
|
||||
Document Root: <?php print($_SERVER['DOCUMENT_ROOT']); ?>
|
||||
</p>
|
||||
<p>
|
||||
<a title="Getting Started" href="https://laragon.org/docs">Getting Started</a>
|
||||
</p>
|
||||
</main>
|
||||
<?php
|
||||
$dirList = glob('*', GLOB_ONLYDIR);
|
||||
if (!empty($dirList)) :
|
||||
?>
|
||||
<nav>
|
||||
<ul>
|
||||
<?php
|
||||
foreach ($dirList as $key => $value) :
|
||||
$link = 'https://' . $value . '.test';
|
||||
?>
|
||||
<a href="<?php echo $link; ?>" target="_blank"><?php echo $link; ?></a>
|
||||
<?php
|
||||
endforeach;
|
||||
?>
|
||||
</ul>
|
||||
</nav>
|
||||
<?php
|
||||
else :
|
||||
?>
|
||||
<aside>
|
||||
<p class="alert">There are no directories, create your first project now</p>
|
||||
<div>
|
||||
<img src="https://i.imgur.com/3Sgu8XI.png" alt="Offline">
|
||||
</div>
|
||||
</aside>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<h1 class="title" title="Laragon">Laragon</h1>
|
||||
<div class="info">
|
||||
<p><?php echo htmlspecialchars($_SERVER['SERVER_SOFTWARE'], ENT_QUOTES, 'UTF-8'); ?></p>
|
||||
<p>PHP version: <?php echo htmlspecialchars(phpversion(), ENT_QUOTES, 'UTF-8'); ?>
|
||||
<a title="phpinfo()" href="/?q=info">info</a>
|
||||
</p>
|
||||
<p>Document Root: <?php echo htmlspecialchars($_SERVER['DOCUMENT_ROOT'], ENT_QUOTES, 'UTF-8'); ?></p>
|
||||
</div>
|
||||
<div class="opt">
|
||||
<p><a title="Getting Started" href="https://laragon.org/docs">Getting Started</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user