Colored output for TTYs, props added
This commit is contained in:
		
							
								
								
									
										78
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								README.md
									
									
									
									
									
								
							@@ -58,6 +58,80 @@ the plugin or library has what is needed, such as checking a define or making
 | 
				
			|||||||
sure that a method or class already exists.
 | 
					sure that a method or class already exists.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Sources and Stubs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Source files are added in the `include` block as either a `file` or a `dir`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    include {
 | 
				
			||||||
 | 
					      dir "directory-to-add";
 | 
				
			||||||
 | 
					      file "file-to-add.php";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The stub is what is invoked when the executable is called, and it is defined in
 | 
				
			||||||
 | 
					the `phar` block, outside of the `include` block that is. The stub must however
 | 
				
			||||||
 | 
					be added through one of the rules in the `include` block.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    stub "src/stub.php";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In the future, it will also be possible to exclude files matching specific
 | 
				
			||||||
 | 
					patterns.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The project properties are a set of key=value items that are defined in the stub,
 | 
				
			||||||
 | 
					and thus made available to the code in the phar. it can be read from a file, or
 | 
				
			||||||
 | 
					generated by a script or executable. Comments should be prefixed with a hash sign
 | 
				
			||||||
 | 
					(`#`):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Comment line
 | 
				
			||||||
 | 
					    MYAPP_VERSION=1.0
 | 
				
			||||||
 | 
					    MYAPP_VARIANT=lite
 | 
				
			||||||
 | 
					    MYAPP_BUILDDATE=2017-03-14
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To add props from a file:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    props "app.props";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To evaluate props when building the archive:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    props exec="generate-props.php";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Options and Tweaks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Boolean options (such as `verbatim` and `library`) will have their value default
 | 
				
			||||||
 | 
					to *true* if omitted. So these two lines will have the same effect:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    verbatim;
 | 
				
			||||||
 | 
					    verbatim true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The following options are currently available. Read the notes before using them
 | 
				
			||||||
 | 
					tho!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### library (bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If set, the created phar will be a pure library, intended to be *included* into
 | 
				
			||||||
 | 
					other PHP projects. The library mode currently depends on composer, and it will
 | 
				
			||||||
 | 
					use `vendor/autoload.php` as the main stub. As such, you need to add any code
 | 
				
			||||||
 | 
					you want executed on load to `autoload/files` in your composer.json.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### verbatim (bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If set, no minification will take place. Generally, the minification should not
 | 
				
			||||||
 | 
					cause any problems but lead to a file that can be up to half a megabyte smaller
 | 
				
			||||||
 | 
					as whitespace and comments are removed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### compress (bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Compress is a legacy option from MakePhar *mk1*, and is not yet implemented. It
 | 
				
			||||||
 | 
					will work in a similar fashion to how it did before: The resulting .phar will
 | 
				
			||||||
 | 
					be compressed, and the output will have a stub prepended to extract the .phar
 | 
				
			||||||
 | 
					into a temporary directory before running. But right now **it does nothing**.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Pro Tips
 | 
					## Pro Tips
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* You can have multiple `phar` blocks in your `makephar.conf`. This lets
 | 
					* You can have multiple `phar` blocks in your `makephar.conf`. This lets
 | 
				
			||||||
@@ -65,5 +139,5 @@ sure that a method or class already exists.
 | 
				
			|||||||
  pick the rule to build tho, but rather all defined phars will be built.
 | 
					  pick the rule to build tho, but rather all defined phars will be built.
 | 
				
			||||||
* MakePhar will minify your files using `php_strip_whitespace()` before adding
 | 
					* MakePhar will minify your files using `php_strip_whitespace()` before adding
 | 
				
			||||||
  them to the archive. This shouldn't be an issue, but if your code depends on
 | 
					  them to the archive. This shouldn't be an issue, but if your code depends on
 | 
				
			||||||
  a specific character index in a specific php file, you might want to use
 | 
					  a specific character index in a specific php file, you might want to specify
 | 
				
			||||||
  another tool for the time being.
 | 
					  the `verbatim` option.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,9 +4,6 @@ phar "makephar.phar" {
 | 
				
			|||||||
    // Set to true to build a library-only phar
 | 
					    // Set to true to build a library-only phar
 | 
				
			||||||
    library false;
 | 
					    library false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Set to 1, 2 or 3 to compress, doesn't work for libraries
 | 
					 | 
				
			||||||
    compress false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Set stub
 | 
					    // Set stub
 | 
				
			||||||
    stub "src/bootstrap.php";
 | 
					    stub "src/bootstrap.php";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,6 +50,9 @@ class Application
 | 
				
			|||||||
            $this->printHelp();
 | 
					            $this->printHelp();
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log_info("MakePhar 2.0 - (c) 2016-2017, NoccyLabs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->config->init) {
 | 
					        if ($this->config->init) {
 | 
				
			||||||
            if (!file_exists("composer.json")) {
 | 
					            if (!file_exists("composer.json")) {
 | 
				
			||||||
                error_log("No composer.json in current directory");
 | 
					                error_log("No composer.json in current directory");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,10 @@ class Manifest
 | 
				
			|||||||
    protected $stub = null;
 | 
					    protected $stub = null;
 | 
				
			||||||
    /** @var bool If true, the archive will be compressed */
 | 
					    /** @var bool If true, the archive will be compressed */
 | 
				
			||||||
    protected $compress = false;
 | 
					    protected $compress = false;
 | 
				
			||||||
 | 
					    /** @var bool If true, the files will be added without minification */
 | 
				
			||||||
 | 
					    protected $verbatim = false;
 | 
				
			||||||
 | 
					    /** @var array Properties to be defined in the main stub */
 | 
				
			||||||
 | 
					    protected $props = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Read manifests from a makephar.sdl file and return all the build targets
 | 
					     * Read manifests from a makephar.sdl file and return all the build targets
 | 
				
			||||||
@@ -51,10 +55,13 @@ class Manifest
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        foreach ($tag->getChildren() as $child) switch ($child->getTagName()) {
 | 
					        foreach ($tag->getChildren() as $child) switch ($child->getTagName()) {
 | 
				
			||||||
            case 'library':
 | 
					            case 'library':
 | 
				
			||||||
                $mf->setIsLibrary($child->getValue());
 | 
					                $mf->setIsLibrary($child->getValue()?:true);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 'compress':
 | 
					            case 'compress':
 | 
				
			||||||
                $mf->setCompression($child->getValue());
 | 
					                $mf->setCompression($child->getValue()?:true);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'verbatim':
 | 
				
			||||||
 | 
					                $mf->setVerbatim($child->getValue()?:true);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 'stub':
 | 
					            case 'stub':
 | 
				
			||||||
                $mf->setStubFile($child->getValue());
 | 
					                $mf->setStubFile($child->getValue());
 | 
				
			||||||
@@ -67,6 +74,38 @@ class Manifest
 | 
				
			|||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'props':
 | 
				
			||||||
 | 
					                if ($src = (string)$child->getValue()) {
 | 
				
			||||||
 | 
					                    if (!file_exists($src)) {
 | 
				
			||||||
 | 
					                        log_warn("Property file %s not found", $src);
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    log_info("Reading props from file %s", $src);
 | 
				
			||||||
 | 
					                    $props = file($src, FILE_SKIP_EMPTY_LINES|FILE_IGNORE_NEW_LINES);
 | 
				
			||||||
 | 
					                } elseif ($script = (string)$child->getAttribute('exec')) {
 | 
				
			||||||
 | 
					                    log_info("Generating props using %s", $script);
 | 
				
			||||||
 | 
					                    if (fnmatch("*.php",$script)) $script = "php {$script}";
 | 
				
			||||||
 | 
					                    exec($script, $props, $ret);
 | 
				
			||||||
 | 
					                    if ($ret>0) {
 | 
				
			||||||
 | 
					                        log_warn("Script exited with code %d", $ret);
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    log_warn("Props specified without either script or src. Properties will not be added.");
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                foreach ($props as $prop) {
 | 
				
			||||||
 | 
					                    $prop = trim($prop);
 | 
				
			||||||
 | 
					                    // skip comments and empty lines
 | 
				
			||||||
 | 
					                    if ($prop && $prop[0]=='#') continue;
 | 
				
			||||||
 | 
					                    if (strpos($prop,'=')===false) continue;
 | 
				
			||||||
 | 
					                    // extract key/value
 | 
				
			||||||
 | 
					                    list ($k,$v) = explode("=",$prop,2);
 | 
				
			||||||
 | 
					                    log_debug("  prop[%s] = %s", strtoupper($k),$v);
 | 
				
			||||||
 | 
					                    $mf->props[strtoupper($k)] = $v;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $mf;
 | 
					        return $mf;
 | 
				
			||||||
@@ -117,6 +156,17 @@ class Manifest
 | 
				
			|||||||
        return $this->compress;
 | 
					        return $this->compress;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function setVerbatim($value)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->verbatim = $value;
 | 
				
			||||||
 | 
					        return $this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getVerbatim()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->verbatim;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function addSource($type, $path, array $opts)
 | 
					    public function addSource($type, $path, array $opts)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->sources[] = (object)[
 | 
					        $this->sources[] = (object)[
 | 
				
			||||||
@@ -151,4 +201,9 @@ class Manifest
 | 
				
			|||||||
        return $items;
 | 
					        return $items;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getProps()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (array)$this->props;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,12 @@ class PharBuilder
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        // Create the phar
 | 
					        // Create the phar
 | 
				
			||||||
        $phar = new \Phar($this->tempFile);
 | 
					        $phar = new \Phar($this->tempFile);
 | 
				
			||||||
        $vo = posix_isatty(STDOUT);
 | 
					        
 | 
				
			||||||
 | 
					        // verbatim add
 | 
				
			||||||
 | 
					        $verbatim = $this->manifest->getVerbatim();
 | 
				
			||||||
 | 
					        if ($verbatim) {
 | 
				
			||||||
 | 
					            log_debug("Note: Creating verbatim archive without minification");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add files
 | 
					        // Add files
 | 
				
			||||||
        log_debug("Adding files to phar archive...");
 | 
					        log_debug("Adding files to phar archive...");
 | 
				
			||||||
@@ -48,20 +53,23 @@ class PharBuilder
 | 
				
			|||||||
        foreach ($this->files as $file) {
 | 
					        foreach ($this->files as $file) {
 | 
				
			||||||
            $i++;
 | 
					            $i++;
 | 
				
			||||||
            $tp = dirname($file->src);
 | 
					            $tp = dirname($file->src);
 | 
				
			||||||
            if ($tp!=$lp) {
 | 
					            if (($tp!=$lp) && (IS_TTY)) {
 | 
				
			||||||
                $lp=$tp;
 | 
					                $lp=$tp;
 | 
				
			||||||
                printf("\r\e[K%d/%d %s", $i, $t, $tp);
 | 
					                printf("\r\e[K%d/%d %s", $i, $t, $tp);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (fnmatch("*.php",$file->src)) {
 | 
					            
 | 
				
			||||||
 | 
					            if (fnmatch("*.php",$file->src) && (!$verbatim)) {
 | 
				
			||||||
                $min = php_strip_whitespace($file->src);
 | 
					                $min = php_strip_whitespace($file->src);
 | 
				
			||||||
                $size_tot += filesize($file->src);
 | 
					                $size_tot += filesize($file->src);
 | 
				
			||||||
                $size_min += strlen($min);
 | 
					                $size_min += strlen($min);
 | 
				
			||||||
                $phar->addFromString($file->dest?:$file->src, $min);
 | 
					                $phar->addFromString($file->dest?:$file->src, $min);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
 | 
					                $size_tot += filesize($file->src);
 | 
				
			||||||
 | 
					                $size_min += filesize($file->src);
 | 
				
			||||||
                $phar->addFile($file->dest?:$file->src, $file->src);
 | 
					                $phar->addFile($file->dest?:$file->src, $file->src);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        ($vo) && printf("\r\e[K");
 | 
					        (IS_TTY) && printf("\r\e[K");
 | 
				
			||||||
        $phar->stopBuffering();
 | 
					        $phar->stopBuffering();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Helper to format sizes
 | 
					        // Helper to format sizes
 | 
				
			||||||
@@ -72,9 +80,13 @@ class PharBuilder
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
        // Format the status
 | 
					        // Format the status
 | 
				
			||||||
        $str_tot = $formatSize($size_tot);
 | 
					        $str_tot = $formatSize($size_tot);
 | 
				
			||||||
 | 
					        if (!$verbatim) {
 | 
				
			||||||
            $str_min = $formatSize($size_min);
 | 
					            $str_min = $formatSize($size_min);
 | 
				
			||||||
            $str_rat = sprintf("%.2f%%", 100/$str_tot*$str_min);
 | 
					            $str_rat = sprintf("%.2f%%", 100/$str_tot*$str_min);
 | 
				
			||||||
            log_debug("Size: %s, Minified: %s (%s)", $str_tot, $str_min, $str_rat);
 | 
					            log_debug("Size: %s, Minified: %s (%s)", $str_tot, $str_min, $str_rat);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            log_debug("Size: %s", $str_tot);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create stub
 | 
					        // Create stub
 | 
				
			||||||
        if ($this->manifest->getIsLibrary()) {
 | 
					        if ($this->manifest->getIsLibrary()) {
 | 
				
			||||||
@@ -82,6 +94,9 @@ class PharBuilder
 | 
				
			|||||||
            // Create library stub
 | 
					            // Create library stub
 | 
				
			||||||
            $stub = '<?php ';
 | 
					            $stub = '<?php ';
 | 
				
			||||||
            $stub.= 'require_once __DIR__."/vendor/autoload.php";';
 | 
					            $stub.= 'require_once __DIR__."/vendor/autoload.php";';
 | 
				
			||||||
 | 
					            foreach ($this->manifest->getProps() as $k=>$v) {
 | 
				
			||||||
 | 
					                $stub.= sprintf('define(%s,%s);', var_export($k,true), var_export($v,true));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if (($stubfile = $this->manifest->getStubFile())) {
 | 
					            if (($stubfile = $this->manifest->getStubFile())) {
 | 
				
			||||||
                $stub.= 'require_once __DIR__."/'.ltrim($stubfile,'/').'";';
 | 
					                $stub.= 'require_once __DIR__."/'.ltrim($stubfile,'/').'";';
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -90,7 +105,11 @@ class PharBuilder
 | 
				
			|||||||
            log_debug("Creating application stub...");
 | 
					            log_debug("Creating application stub...");
 | 
				
			||||||
            // Write application stub
 | 
					            // Write application stub
 | 
				
			||||||
            $stubfile = $this->manifest->getStubFile();
 | 
					            $stubfile = $this->manifest->getStubFile();
 | 
				
			||||||
            $stub = '<?php require_once __DIR__."/'.ltrim($stubfile,'/').'";';
 | 
					            $stub = '<?php ';
 | 
				
			||||||
 | 
					            foreach ($this->manifest->getProps() as $k=>$v) {
 | 
				
			||||||
 | 
					                $stub.= sprintf('define(%s,%s);', var_export($k,true), var_export($v,true));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $stub.= 'require_once __DIR__."/'.ltrim($stubfile,'/').'";';
 | 
				
			||||||
            $phar->addFromString("index.php", $stub);
 | 
					            $phar->addFromString("index.php", $stub);
 | 
				
			||||||
            // Make the phar stub executable
 | 
					            // Make the phar stub executable
 | 
				
			||||||
            $mainstub = $phar->createDefaultStub("index.php");
 | 
					            $mainstub = $phar->createDefaultStub("index.php");
 | 
				
			||||||
@@ -116,7 +135,7 @@ class PharBuilder
 | 
				
			|||||||
        if (file_exists($output)) {
 | 
					        if (file_exists($output)) {
 | 
				
			||||||
            unlink($output);
 | 
					            unlink($output);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        log_debug("Writing %s", $output);
 | 
					        log_info("Writing %s", $output);
 | 
				
			||||||
        rename($this->tempFile, $output);
 | 
					        rename($this->tempFile, $output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!$this->manifest->getIsLibrary()) {
 | 
					        if (!$this->manifest->getIsLibrary()) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,21 @@ class LogFuncs {
 | 
				
			|||||||
        if ($bound++) return;
 | 
					        if ($bound++) return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function log_info($fmt,...$arg) { printf("[info] {$fmt}\n", ...$arg); }
 | 
					
 | 
				
			||||||
function log_debug($fmt,...$arg) { printf("[debg] {$fmt}\n", ...$arg); }
 | 
					if (@empty(STDOUT) || (!posix_isatty(STDOUT))) {
 | 
				
			||||||
function log_warn($fmt,...$arg) { printf("[warn] {$fmt}\n", ...$arg); }
 | 
					    define("LF_INFO", "");
 | 
				
			||||||
 | 
					    define("LF_DEBUG", "");
 | 
				
			||||||
 | 
					    define("LF_WARN", "");
 | 
				
			||||||
 | 
					    define("LF_RESET", "");
 | 
				
			||||||
 | 
					    define("IS_TTY", false);
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
					    define("LF_INFO", "\e[32;1m");
 | 
				
			||||||
 | 
					    define("LF_DEBUG", "\e[32m");
 | 
				
			||||||
 | 
					    define("LF_WARN", "\e[31;1m");
 | 
				
			||||||
 | 
					    define("LF_RESET", "\e[0m");
 | 
				
			||||||
 | 
					    define("IS_TTY", true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function log_info($fmt,...$arg) { printf(LF_INFO."[info] {$fmt}".LF_RESET."\n", ...$arg); }
 | 
				
			||||||
 | 
					function log_debug($fmt,...$arg) { printf(LF_DEBUG."[debg] {$fmt}".LF_RESET."\n", ...$arg); }
 | 
				
			||||||
 | 
					function log_warn($fmt,...$arg) { printf(LF_WARN."[warn] {$fmt}".LF_RESET."\n", ...$arg); }
 | 
				
			||||||
		Reference in New Issue
	
	Block a user