I'm using longman/telegram-bot package at my Yii2 project.
class GenericmessageCommand extends SystemCommand
{
/**
* #var string
*/
protected $name = 'genericmessage';
/**
* #var string
*/
protected $description = 'Handle generic message';
/**
* #var string
*/
protected $version = '1.0.0';
/**
* Main command execution
*
* #return ServerResponse
*/
public function execute(): ServerResponse
{
$message = $this->getMessage();
if (in_array($message->getType(), ['audio', 'document', 'photo', 'video', 'voice'])) {
$doc = call_user_func([$message, 'get' . $message->getType()]);
($message->getType() === 'photo') && $doc = end($doc);
$photoId = $doc->file_id;
$download_path = $this->telegram->getDownloadPath();
$file = Request::getFile(['file_id' => $photoId]);
if ($file->isOk() && Request::downloadFile($file->getResult())) {
return $this->replyToChat(' file is located at: ' . $download_path . '/' . $file->getResult()->getFilePath());
} else {
return $this->replyToChat('Failed to download.');
}
}
}
}
Just like that
It working fine when I'm using getUpdates method https://github.com/php-telegram-bot/core#getupdates-installation
But it doesn't work when I use WebHooks. Even though I get the same answer from my bot... It says "Ok" and "file is located at...", but there is no such file.
that because of webhook using backend of Yii2 advanced.... it stores all files at backend (because my webhook looking on backend), but i searching them at frontend (bacause i'm stupid).
Things to note, I have tested this on two different servers, Debian 9 and Ubuntu 14.04 and the same error persists. Right now I am using Ubuntu 14.04 with PHP 5, I have installed composer, I have installed both wkhtmltopdf and phpwkhtmltopdf correctly. How do I know this? Well wkhtmltopdf/image works via CLI, phpwkhtmltopdf also works via PHP however when I attempt to send the image to the client as an inline display or download the image corrupts. For example;
Visit desired url, for us its test.php
Phpwkhtmltopdf will send commands that hook up with the CLI version wkhtmltopdf
Once the page loads it will visit google.com, save a screenshot on the disk /var/www/html/tmp/page.jpg and that image opens/displays fine, however when I attempt to use $image->send('page.jpg'); the sent image is corrupt/wont open.
I have made two changes to the system, I have disabled mod_deflate within apache2 and I have also increased the max_filesize options within apache2's php.ini config file.
Dependencies
wkhtmltopdf - https://wkhtmltopdf.org/
phpwkhtmltopdf - https://github.com/mikehaertl/phpwkhtmltopdf
Live Example
http://155.254.35.63/test.php // Generate the image
http://155.254.35.63/tmp/page.png // The image file generated
test.php
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
?>
<?php
$loader = require __DIR__ . '/vendor/autoload.php';
?>
<?php
use mikehaertl\wkhtmlto\Image;
$image = new \mikehaertl\wkhtmlto\Image('https://www.google.co.uk/search?q=what+is+the+time&oq=what+is+the+time&aqs=chrome.0.69i59j69i60l3j0l2.2977j0j4&sourceid=chrome&ie=UTF-8');
$image->setOptions(array(
'binary' => '/usr/local/bin/wkhtmltoimage',
'type' => 'png'
));
$image->saveAs('/var/www/html/tmp/page.png');
header('Content-Type: image/png');
echo file_get_contents('/var/www/html/tmp/page.jpg');
?>
File.php (Lines 72 to 83)
<?php
namespace mikehaertl\tmp;
/**
* File
*
* A convenience class for temporary files.
*
* #author Michael Härtl <haertl.mike#gmail.com>
* #version 1.1.0
* #license http://www.opensource.org/licenses/MIT
*/
class File
{
/**
* #var bool whether to delete the tmp file when it's no longer referenced or when the request ends.
* Default is `true`.
*/
public $delete = true;
/**
* #var string the name of this file
*/
protected $_fileName;
/**
* Constructor
*
* #param string $content the tmp file content
* #param string|null $suffix the optional suffix for the tmp file
* #param string|null $prefix the optional prefix for the tmp file. If null 'php_tmpfile_' is used.
* #param string|null $directory directory where the file should be created. Autodetected if not provided.
*/
public function __construct($content, $suffix = null, $prefix = null, $directory = null)
{
if ($directory===null) {
$directory = self::getTempDir();
}
if ($prefix===null) {
$prefix = 'php_tmpfile_';
}
$this->_fileName = tempnam($directory,$prefix);
if ($suffix!==null) {
$newName = $this->_fileName.$suffix;
rename($this->_fileName, $newName);
$this->_fileName = $newName;
}
file_put_contents($this->_fileName, $content);
}
/**
* Delete tmp file on shutdown if `$delete` is `true`
*/
public function __destruct()
{
if ($this->delete) {
unlink($this->_fileName);
}
}
/**
* Send tmp file to client, either inline or as download
*
* #param string|null $filename the filename to send. If empty, the file is streamed inline.
* #param string $contentType the Content-Type header
* #param bool $inline whether to force inline display of the file, even if filename is present.
*/
public function send($filename = null, $contentType, $inline = false)
{
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Type: image/png');
header('Content-Transfer-Encoding: binary');
if ($filename!==null || $inline) {
$disposition = $inline ? 'inline' : 'attachment';
header("Content-Disposition: $disposition; filename=\"$filename\"");
}
readfile($this->_fileName);
}
/**
* #param string $name the name to save the file as
* #return bool whether the file could be saved
*/
public function saveAs($name)
{
return copy($this->_fileName, $name);
}
/**
* #return string the full file name
*/
public function getFileName()
{
return $this->_fileName;
}
/**
* #return string the path to the temp directory
*/
public static function getTempDir()
{
if (function_exists('sys_get_temp_dir')) {
return sys_get_temp_dir();
} elseif ( ($tmp = getenv('TMP')) || ($tmp = getenv('TEMP')) || ($tmp = getenv('TMPDIR')) ) {
return realpath($tmp);
} else {
return '/tmp';
}
}
/**
* #return string the full file name
*/
public function __toString()
{
return $this->_fileName;
}
}
php.ini
;;;;;;;;;;;;;;;;
; File Uploads ;
;;;;;;;;;;;;;;;;
; Whether to allow HTTP file uploads.
; http://php.net/file-uploads
file_uploads = On
; Temporary directory for HTTP uploaded files (will use system default if not
; specified).
; http://php.net/upload-tmp-dir
;upload_tmp_dir = /var/www/html/tmp
; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
upload_max_filesize = 50M
; Maximum number of files that can be uploaded via a single request
max_file_uploads = 20
There are none (0) errors in the apache log file, which is really putting me off what the issue could be. I have attempted to find a resolution with the dev but no look;
https://github.com/mikehaertl/phpwkhtmltopdf/issues/278
Id appreciate the help on this one.
I have examined the page.jpg file your test site generates. The file itself is intact. This means there is nothing wrong with your plumbing.
The file header shows that instead of a standard JPEG file, yours is a JFIF variant. See if you can set the library to generate a PNG file to workaround this issue.
Edit: now that I see the generated file is correct, see if you can just stream the content instead of using $image->send. Send it youself:
header('Content-Type: image/png');
echo file_get_contents('/var/www/html/tmp/page.jpg');
I would like to use YUI compressor with minify PHP rather than the default JSmin. Does anyone have experience setting this up?
Right now I am using the groupsConfig.php to combine the JS.
return array(
'jsAll' => array('//contenido/themes/bam/assets/js/jquery.js', '//contenido/themes/bam/assets/js/modernizr.js','//contenido/themes/bam/assets/js/imgpreload.js', '//contenido/themes/bam/assets/js/imgpreload.js', '//contenido/themes/bam/assets/js/history.js','//contenido/themes/bam/assets/js/ajaxify.js', '//contenido/themes/bam/assets/js/isotope.js'),
'jsHome' => array('//contenido/themes/bam/assets/js/easing.js','//contenido/themes/bam/assets/js/scrollable.js', '//contenido/themes/bam/assets/js/home.js'),
'cssAll' => array('//contenido/themes/bam/bam.css'),
);
As it says on the homepage:
Uses an enhanced port of Douglas Crockford's JSMin library and custom classes to minify CSS and HTML
I have the following code in config.php, but I get a 500 error when trying to view the combined js file:
function yuiJs($js) {
require_once '/lib/Minify/YUICompressor.php';
Minify_YUICompressor::$jarFile = '/lib/yuicompressor-2.4.2.jar';
Minify_YUICompressor::$tempDir = '/temp';
return Minify_YUICompressor::minifyJs($js);
}
$min_serveOptions['minifiers']['application/x-javascript'] = 'yuiJs';
It also appears that there are several lines in lib/Minify/YUICompressor.php that need to be configured, and I'm not sure if I'm doing it right:
class Minify_YUICompressor {
/**
* Filepath of the YUI Compressor jar file. This must be set before
* calling minifyJs() or minifyCss().
*
* #var string
*/
public static $jarFile = '../yuicompressor-2.4.2.jar';
/**
* Writable temp directory. This must be set before calling minifyJs()
* or minifyCss().
*
* #var string
*/
public static $tempDir = '../../temp/';
/**
* Filepath of "java" executable (may be needed if not in shell's PATH)
*
* #var string
*/
public static $javaExecutable = 'java';
I had the same problem on windows. It seems jar file needs to be executable in order to run yui compressor. So, i have to remove excutable check from YUICompressor.php
#132
private static function _prepare()
{
if (! is_file(self::$jarFile)) {
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.');
}
// if (! is_executable(self::$jarFile)) {
// throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not executable.');
// }
if (! is_dir(self::$tempDir)) {
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.');
}
if (! is_writable(self::$tempDir)) {
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not writable.');
}
}
and that works fine.
Even after a very high score of Google PageSpeed(97) & Yahoo! YSlow(92) the PHP generated thumbnails don't seem to be coming passively from an old cache: they seem to be generated every time again...and again... freshly baked consuming lots of waisted time.
This question will focus only & specifically on how to solve the CACHE problem of the PHP Code that generates the thumbs:
Just have a look at these tiny puny little thumbnails measuring only 3 ~ 5 kb each!
Waterfall in detail: http://www.webpagetest.org/result/110328_AM_8T00/1/details/
Any & all suggestons are +1 help to me and warmly welcome, for I have grown quite desperate on this issue for the last months. Thanx a Thousand!
Using or not Modrewrite does not influence speed: both are same. I use these rewrite conditions: RewriteCond %{REQUEST_URI} ^/IMG-.*$ & RewriteCond %{REQUEST_FILENAME} !-f
Both the original default URL as well as the beautified rewritten URL produce the same delays!! So let us not point the fault to the lightning fast Apache: its the PHP Cache / headers that are somehow wrongly coded...
Warning by webpagetest.org: Leverage browser caching of static assets: 69/100
FAILED - (No max-age or expires): http://aster.nu/imgcpu?src=aster_bg/124.jpg&w=1400&h=100&c=p
After each refresh, you will see either of these two warnings appear on random at REDbot.org
Relevant Portions of The Code:
// Script is directly called
if(isset($_GET['src']) && (isset($_GET['w']) || isset($_GET['h']) || isset($_GET['m']) || isset($_GET['f']) || isset($_GET['q']))){
$ImageProcessor = new ImageProcessor(true);
$ImageProcessor->Load($_GET['src'], true);
$ImageProcessor->EnableCache("/var/www/vhosts/blabla.org/httpdocs/tmp/", 345600);
$ImageProcessor->Parse($quality);
}
/* Images processing class
* - create image thumbnails on the fly
* - Can be used with direct url imgcpu.php?src=
* - Cache images for efficiency
*/
class ImageProcessor
{
private $_image_path; # Origninal image path
protected $_image_name; # Image name string
private $_image_type; # Image type int
protected $_mime; # Image mime type string
private $_direct_call = false; # Is it a direct url call? boolean
private $_image_resource; # Image resource var Resource
private $_cache_folder; # Cache folder strig
private $_cache_ttl; # Cache time to live int
private $_cache = false; # Cache on boolean
private $_cache_skip = false; # Cache skip var boolean
private function cleanUrl($image){ # Cleanup url
$cimage = str_replace("\\", "/", $image);
return $cimage;
}
/** Get image resource
* #access private, #param string $image, #param string $extension, #return resource */
private function GetImageResource($image, $extension){
switch($extension){
case "jpg":
#ini_set('gd.jpeg_ignore_warning', 1);
$resource = imagecreatefromjpeg($image);
break;
}
return $resource;
}
/* Save image to cache folder
* #access private, #return void */
private function cacheImage($name, $content){
# Write content file
$path = $this->_cache_folder . $name;
$fh = fopen($path, 'w') or die("can't open file");
fwrite($fh, $content);
fclose($fh);
# Delete expired images
foreach (glob($this->_cache_folder . "*") as $filename) {
if(filemtime($filename) < (time() - $this->_cache_ttl)){
unlink( $filename );
}
}
}
/* Get an image from cache
* #access public, #param string $name, #return void */
private function cachedImage($name){
$file = $this->_cache_folder . $name;
$fh = fopen($file, 'r');
$content = fread($fh, filesize($file));
fclose($fh);
return $content;
}
/* Get name of the cache file
* #access private, #return string */
private function generateCacheName(){
$get = implode("-", $_GET);
return md5($this->_resize_mode . $this->_image_path . $this->_old_width . $this->_old_height . $this->_new_width . $this->_new_height . $get) . "." . $this->_extension;
}
/* Check if a cache file is expired
* #access private, #return bool */
private function cacheExpired(){
$path = $this->_cache_folder . $this->generateCacheName();
if(file_exists($path)){
$filetime = filemtime($path);
return $filetime < (time() - $this->_cache_ttl);
}else{
return true;
}
}
/* Lazy load the image resource needed for the caching to work
* #return void */
private function lazyLoad(){
if(empty($this->_image_resource)){
if($this->_cache && !$this->cacheExpired()){
$this->_cache_skip = true;
return;
}
$resource = $this->GetImageResource($this->_image_path, $this->_extension);
$this->_image_resource = $resource;
}
}
/* Constructor
* #access public, #param bool $direct_call, #return void */
public function __construct($direct_call=false){
# Check if GD extension is loaded
if (!extension_loaded('gd') && !extension_loaded('gd2')) {
$this->showError("GD is not loaded");
}
$this->_direct_call = $direct_call;
}
/* Resize
* #param int $width, #param int $height, #param define $mode
* #param bool $auto_orientation houd rekening met orientatie wanneer er een resize gebeurt */
public function Resize($width=100, $height=100, $mode=RESIZE_STRETCH, $auto_orientation=false){
// Validate resize mode
$valid_modes = array("f", "p");
}
// .... omitted .....
// Set news size vars because these are used for the
// cache name generation
// .... omitted .....
$this->_old_width = $width;
$this->_old_height = $height;
// Lazy load for the directurl cache to work
$this->lazyLoad();
if($this->_cache_skip) return true;
// Create canvas for the new image
$new_image = imagecreatetruecolor($width, $height);
imagecopyresampled($new_image, $this->_image_resource, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
// .... omitted .....
$this->_image_resource = $new_image;
}
/* Create image resource from path or url
* #access public, #param string $location, #param bool $lazy_load, #return */
public function Load($image,$lazy_load=false){
// Cleanup image url
$image = $this->cleanUrl($image);
// Check if it is a valid image
if(isset($mimes[$extension]) && ((!strstr($image, "http://") && file_exists($image)) || strstr($image, "http://")) ){
// Urlencode if http
if(strstr($image, "http://")){
$image = str_replace(array('http%3A%2F%2F', '%2F'), array('http://', '/'), urlencode($image));
}
$image = str_replace("+", "%20", $image);
$this->_extension = $extension;
$this->_mime = $mimes[$extension];
$this->_image_path = $image;
$parts = explode("/", $image);
$this->_image_name = str_replace("." . $this->_extension, "", end($parts));
// Get image size
list($width, $height, $type) = getimagesize($image);
$this->_old_width = $width;
$this->_old_height = $height;
$this->_image_type = $type;
}else{
$this->showError("Wrong image type or file does not exists.");
}
if(!$lazy_load){
$resource = $this->GetImageResource($image, $extension);
$this->_image_resource = $resource;
}
}
/* Save image to computer
* #access public, #param string $destination, #return void */
public function Save($destination, $quality=60){
if($this->_extension == "png" || $this->_extension == "gif"){
imagesavealpha($this->_image_resource, true);
}
switch ($this->_extension) {
case "jpg": imagejpeg($this->_image_resource,$destination, $quality); break;
case "gif": imagegif($this->_image_resource,$destination); break;
default: $this->showError('Failed to save image!'); break;
}
}
/* Print image to screen
* #access public, #return void */
public function Parse($quality=60){
$name = $this->generateCacheName();
$content = "";
if(!$this->_cache || ($this->_cache && $this->cacheExpired())){
ob_start();
header ("Content-type: " . $this->_mime);
if($this->_extension == "png" || $this->_extension == "gif"){
imagesavealpha($this->_image_resource, true);
}
switch ($this->_extension) {
case "jpg": imagejpeg($this->_image_resource, "", $quality); break;
case "gif": imagegif($this->_image_resource); break;
default: $this->showError('Failed to save image!'); break;
}
$content = ob_get_contents();
ob_end_clean();
}else{
if (isset ($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
if (strtotime ($_SERVER['HTTP_IF_MODIFIED_SINCE']) < strtotime('now')) {
header ('HTTP/1.1 304 Not Modified');
die ();
}
}
// change the modified headers
$gmdate_expires = gmdate ('D, d M Y H:i:s', strtotime ('now +10 days')) . ' GMT';
$gmdate_modified = gmdate ('D, d M Y H:i:s') . ' GMT';
header ("Content-type: " . $this->_mime);
header ('Accept-Ranges: bytes');
header ('Last-Modified: ' . $gmdate_modified);
header ('Cache-Control: max-age=864000, must-revalidate');
header ('Expires: ' . $gmdate_expires);
echo $this->cachedImage($name);
exit();
}
// Save image content
if(!empty($content) && $this->_cache){
$this->cacheImage($name, $content);
}
// Destroy image
$this->Destroy();
echo $content;
exit();
}
/* Destroy resources
* #access public, #return void */
public function Destroy(){
imagedestroy($this->_image_resource);
}
/* Get image resources
* #access public, #return resource */
public function GetResource(){
return $this->_image_resource;
}
/* Set image resources
* #access public, #param resource $image, #return resource */
public function SetResource($image){
$this->_image_resource = $image;
}
/* Enable caching
* #access public, #param string $folder, #param int $ttl, * #return void */
public function EnableCache($folder="/var/www/vhosts/blabla.org/httpdocs/tmp/", $ttl=345600){
if(!is_dir($folder)){
$this->showError("Directory '" . $folder . "' does'nt exist");
}else{
$this->_cache = true;
$this->_cache_folder = $folder;
$this->_cache_ttl = $ttl;
}
return false;
}
}
The original author granted me permission for placing parts of code in here for solving this issue.
If I'm understanding the question correctly, this is entirely to be expected. Image manipulation is slow.
The yellow is your browser sending the request. The green is your browser waiting on the server to actually create the thumbnail, which takes a very significant amount of time, no matter what library the server is using. The blue is the server sending the response, which, unlike the previous steps, is affected by filesize.
There's not much to be done about the inherent slowness of image manipulation. It would be wise to cache these thumbnails so that they are only generated once and are then served statically. That way, very few of your users will ever have to sit through that green delay, and your server will be happy, too.
EDIT: If the issue is that the files exist at those URLs, but your RewriteRule is kicking in anyway, bear in mind that, by default, rules run without checking if the file exists.
Use the following condition above your RewriteRule to make sure the file exists.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule # ...etc...
imgcpu.php?src=foo/foo.jpg&w=100&h=100
so imgcpu.php is running for every image request?
In that case, if you are worried about performance,:
the script needs to do some caching of the thumbnails it creates. If it resizes stuff on every request, that's your problem right there.
the script needs to send some caching headers to the browser - a pure PHP script won't do that, and will be refreshed on every page load
a session_start() call inside the PHP script could lead to concurrency issues because of session locking.
you will need to show some PHP code. Maybe in a separate question, though.
Apache can serve up files from your hard disk a lot faster than PHP can, and it appears that you're doing the latter to handle caching:
/**
* Get an image from cache
*
* #access public
* #param string $name
* #return void
*/
private function cachedImage($name){
$file = $this->_cache_folder . $name;
$fh = fopen($file, 'r');
$content = fread($fh, filesize($file));
fclose($fh);
return $content;
}
There's a better way of doing what that function is doing (passthru), but the best option is to setup a regex that'll only rewrite a request to your thumbnailing script if the file doesn't already exist:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^images/.*$ - [NC,L]
RewriteRule ^images/(.*)$ /imgcpu.php/$1 [NC,L]
And then introduce logic to parse the request to an image and format it accordingly. For example, you could say thumbs should be named after the original file and have the W x H dimensions appended like "stackoverflow_logo_100x100.jpg".
Make sense?
Per request (in the comment), the description of the "s", "l", and "d" flags is as follows (quoting the docs):
'-d' (is directory) Treats the
TestString as a pathname and tests
whether or not it exists, and is a
directory.
'-s' (is
regular file, with size) Treats the
TestString as a pathname and tests
whether or not it exists, and is a
regular file with size greater than
zero.
'-l' (is symbolic link) Treats
the TestString as a pathname and tests
whether or not it exists, and is a
symbolic link.
Your checking your HTTP_IF_MODIFIED_SINCE header and cache AFTER you generate the image so the image is getting generated and cached every time you load the page. You would get a considerable decrease in time if you move these checks closer to the start of execution, before you start processing the image.
Matchu gave you answer why. If you want to fix it, save the created thumbnails, so they are not recreated on each request. I use simple 404 page that catches request to thumbnails that haven't been created, that script figures out the required dimensions and file from url - eg /thumbs/100x100/cat.png means create 100x100 thumbnail from /images/cat.png.
I need to create CRX file on the fly. It's for my CMS backend, so it will be just for authenticated users who can install CMS backend as webapp and offer some more privileges to the web app. The problem is, that the backend is used for many domains so creating CRX file for each of them is quite a work. So I figured that it would be easier to just create CRX file on demand which would be generated by PHP using its own domain and probably custom icon.
On the documentation page, they explain the CRX package format. There are many third party libraries that implemented that format. In the following page, you can learn the format and either download a Ruby / Bash script (you can find others too online), and if you want to implement your own packager, you can follow the format described there.
https://developer.chrome.com/extensions/crx
If you really don't want to follow the format, you can let your PHP script do one of the following:
Use Chrome binary chrome.exe --pack-extension=c:\myext --pack-extension-key=c:\myext.pem
Use the Ruby or Bash script from PHP (you can call system commands)
Hope that helps!
Also, for anyone still looking for a way to create a CTX in PHP, look at this question: Create Google Chrome Crx file with PHP
This works for me :D I just change from real path to null without that changes won't work on new chrome :D
/**
* Class CrxGenerator
*
* Create Chrome Extension CRX packages from
* folder & pem private key
*
* Based on CRX format documentation: http://developer.chrome.com/extensions/crx.html
*
* #author: Tomasz Banasiak
* #license: MIT
* #date: 2013-11-03
*/
class CrxGenerator {
const TEMP_ARCHIVE_EXT = '.zip';
private $sourceDir = null;
private $cacheDir = '';
private $privateKeyContents = null;
private $publicKeyContents = null;
private $privateKey = null;
private $publicKey = null;
/**
* #param $file Path to PEM key
* #throws Exception
*/
public function setPrivateKey($file) {
if (!file_exists($file)) {
throw new Exception('Private key file does not exist');
}
$this->privateKeyContents = file_get_contents($file);
$this->privateKey = $file;
}
/**
* #param $file Path to PUB key
* #throws Exception
*/
public function setPublicKey($file) {
if (!file_exists($file)) {
throw new Exception('Private key file does not exist');
}
$this->publicKeyContents = file_get_contents($file);
$this->publicKey = $file;
}
/**
* #param $cacheDir dir specified for caching temporary archives
* #throws Exception
*/
public function setCacheDir($cacheDir) {
if (!is_dir($cacheDir)) {
throw new Exception('Cache dir does not exist!');
}
$this->cacheDir = $cacheDir;
}
/**
* #param $sourceDir Extension source directory
*/
public function setSourceDir($sourceDir) {
$this->sourceDir = $sourceDir;
}
/**
* #param $outputFile path to output file
* #throws Exception
*/
public function generateCrx($outputFile) {
$basename = basename($outputFile);
// First step - create ZIP archive
$zipArchive = $this->cacheDir . DIRECTORY_SEPARATOR . $basename . self::TEMP_ARCHIVE_EXT;
$result = $this->createZipArchive(
$this->sourceDir,
$zipArchive
);
if (!$result) {
throw new Exception('ZIP creation failed');
}
$zipContents = file_get_contents($zipArchive);
// Second step - create file signature
$privateKey = openssl_pkey_get_private($this->privateKeyContents);
openssl_sign($zipContents, $signature, $privateKey, 'sha1');
openssl_free_key($privateKey);
// Create output file
$crx = fopen($outputFile, 'wb');
fwrite($crx, 'Cr24');
fwrite($crx, pack('V', 2));
fwrite($crx, pack('V', strlen($this->publicKeyContents)));
fwrite($crx, pack('V', strlen($signature)));
fwrite($crx, $this->publicKeyContents);
fwrite($crx, $signature);
fwrite($crx, $zipContents);
fclose($crx);
// Clear cache
unset($zipArchive);
}
/**
* #param $source - source dir
* #param $outputFile - output file
* #return bool - success?
*/
private function createZipArchive($source, $outputFile) {
if (!extension_loaded('zip') || !file_exists($source)) {
return false;
}
$zip = new ZipArchive();
if (!$zip->open($outputFile, ZIPARCHIVE::CREATE)) {
return false;
}
$source = str_replace('\\', '/', realpath($source));
if (is_dir($source) === true) {
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
foreach ($files as $file) {
$file = str_replace('\\', '/', $file);
// Exclude "." and ".." folders
if( in_array(substr($file, strrpos($file, '/') + 1), array('.', '..')) ) {
continue;
}
$file = $file;
if (is_dir($file) === true) {
$zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
}
else if (is_file($file) === true) {
$zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
}
}
}
else if (is_file($source) === true) {
$zip->file_get_contents($source);
echo $source;
}
return $zip->close();
}
}
Looks like I have found exactly what I was looking for. Chrome team has made this option to create CRX-less web apps, just by using simple manifest file.
Much easier to create own webapp and publish it on website for installation. And it also solves my problem when I have many websites with a lot of domains and I don't have to create custom CRX file for each domain. I just create small PHP script which creates manifest files on the fly for each domain.