Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F1432218
ConsoleProcessus.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
20 KB
Referenced Files
None
Subscribers
None
ConsoleProcessus.php
View Options
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS 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 COPYRIGHT HOLDERS AND 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.
*/
namespace
Psy\Readline\Hoa
;
/**
* Manipulate a processus as a stream.
*/
class
ConsoleProcessus
extends
Stream
implements
StreamIn
,
StreamOut
,
StreamPathable
{
/**
* Signal: terminal line hangup (terminate process).
*/
const
SIGHUP
=
1
;
/**
* Signal: interrupt program (terminate process).
*/
const
SIGINT
=
2
;
/**
* Signal: quit program (create core image).
*/
const
SIGQUIT
=
3
;
/**
* Signal: illegal instruction (create core image).
*/
const
SIGILL
=
4
;
/**
* Signal: trace trap (create core image).
*/
const
SIGTRAP
=
5
;
/**
* Signal: abort program, formerly SIGIOT (create core image).
*/
const
SIGABRT
=
6
;
/**
* Signal: emulate instruction executed (create core image).
*/
const
SIGEMT
=
7
;
/**
* Signal: floating-point exception (create core image).
*/
const
SIGFPE
=
8
;
/**
* Signal: kill program (terminate process).
*/
const
SIGKILL
=
9
;
/**
* Signal: bus error.
*/
const
SIGBUS
=
10
;
/**
* Signal: segmentation violation (create core image).
*/
const
SIGSEGV
=
11
;
/**
* Signal: non-existent system call invoked (create core image).
*/
const
SIGSYS
=
12
;
/**
* Signal: write on a pipe with no reader (terminate process).
*/
const
SIGPIPE
=
13
;
/**
* Signal: real-time timer expired (terminate process).
*/
const
SIGALRM
=
14
;
/**
* Signal: software termination signal (terminate process).
*/
const
SIGTERM
=
15
;
/**
* Signal: urgent condition present on socket (discard signal).
*/
const
SIGURG
=
16
;
/**
* Signal: stop, cannot be caught or ignored (stop proces).
*/
const
SIGSTOP
=
17
;
/**
* Signal: stop signal generated from keyboard (stop process).
*/
const
SIGTSTP
=
18
;
/**
* Signal: continue after stop (discard signal).
*/
const
SIGCONT
=
19
;
/**
* Signal: child status has changed (discard signal).
*/
const
SIGCHLD
=
20
;
/**
* Signal: background read attempted from control terminal (stop process).
*/
const
SIGTTIN
=
21
;
/**
* Signal: background write attempted to control terminal (stop process).
*/
const
SIGTTOU
=
22
;
/**
* Signal: I/O is possible on a descriptor, see fcntl(2) (discard signal).
*/
const
SIGIO
=
23
;
/**
* Signal: cpu time limit exceeded, see setrlimit(2) (terminate process).
*/
const
SIGXCPU
=
24
;
/**
* Signal: file size limit exceeded, see setrlimit(2) (terminate process).
*/
const
SIGXFSZ
=
25
;
/**
* Signal: virtual time alarm, see setitimer(2) (terminate process).
*/
const
SIGVTALRM
=
26
;
/**
* Signal: profiling timer alarm, see setitimer(2) (terminate process).
*/
const
SIGPROF
=
27
;
/**
* Signal: Window size change (discard signal).
*/
const
SIGWINCH
=
28
;
/**
* Signal: status request from keyboard (discard signal).
*/
const
SIGINFO
=
29
;
/**
* Signal: User defined signal 1 (terminate process).
*/
const
SIGUSR1
=
30
;
/**
* Signal: User defined signal 2 (terminate process).
*/
const
SIGUSR2
=
31
;
/**
* Command name.
*/
protected
$_command
=
null
;
/**
* Command options (options => value, or input).
*/
protected
$_options
=
[];
/**
* Current working directory.
*/
protected
$_cwd
=
null
;
/**
* Environment.
*/
protected
$_environment
=
null
;
/**
* Timeout.
*/
protected
$_timeout
=
30
;
/**
* Descriptor.
*/
protected
$_descriptors
=
[
0
=>
[
'pipe'
,
'r'
],
1
=>
[
'pipe'
,
'w'
],
2
=>
[
'pipe'
,
'w'
],
];
/**
* Pipe descriptors of the processus.
*/
protected
$_pipes
=
null
;
/**
* Seekability of pipes.
*/
protected
$_seekable
=
[];
/**
* Start a processus.
*/
public
function
__construct
(
string
$command
,
?
array
$options
=
null
,
?
array
$descriptors
=
null
,
?
string
$cwd
=
null
,
?
array
$environment
=
null
,
int
$timeout
=
30
)
{
$this
->
setCommand
(
$command
);
if
(
null
!==
$options
)
{
$this
->
setOptions
(
$options
);
}
if
(
null
!==
$descriptors
)
{
$this
->
_descriptors
=
[];
foreach
(
$descriptors
as
$descriptor
=>
$nature
)
{
if
(
isset
(
$this
->
_descriptors
[
$descriptor
]))
{
throw
new
ConsoleException
(
'Pipe descriptor %d already exists, cannot '
.
'redefine it.'
,
0
,
$descriptor
);
}
$this
->
_descriptors
[
$descriptor
]
=
$nature
;
}
}
$this
->
setCwd
(
$cwd
?:
\getcwd
());
if
(
null
!==
$environment
)
{
$this
->
setEnvironment
(
$environment
);
}
$this
->
setTimeout
(
$timeout
);
parent
::
__construct
(
$this
->
getCommandLine
(),
null
,
true
);
$this
->
getListener
()->
addIds
([
'input'
,
'output'
,
'timeout'
,
'start'
,
'stop'
]);
return
;
}
/**
* Open the stream and return the associated resource.
*/
protected
function
&
_open
(
string
$streamName
,
?
StreamContext
$context
=
null
)
{
$out
=
@
\proc_open
(
$streamName
,
$this
->
_descriptors
,
$this
->
_pipes
,
$this
->
getCwd
(),
$this
->
getEnvironment
()
);
if
(
false
===
$out
)
{
throw
new
ConsoleException
(
'Something wrong happen when running %s.'
,
1
,
$streamName
);
}
return
$out
;
}
/**
* Close the current stream.
*/
protected
function
_close
():
bool
{
foreach
(
$this
->
_pipes
as
$pipe
)
{
@
\fclose
(
$pipe
);
}
return
(
bool
)
@
\proc_close
(
$this
->
getStream
());
}
/**
* Run the process and fire events (amongst start, stop, input, output and
* timeout).
* If an event returns false, it will close the current pipe.
* For a simple run without firing events, use the $this->open() method.
*/
public
function
run
()
{
if
(
false
===
$this
->
isOpened
())
{
$this
->
open
();
}
else
{
$this
->
_close
();
$this
->
_setStream
(
$this
->
_open
(
$this
->
getStreamName
(),
$this
->
getStreamContext
()
));
}
$this
->
getListener
()->
fire
(
'start'
,
new
EventBucket
());
$_read
=
[];
$_write
=
[];
$_except
=
[];
foreach
(
$this
->
_pipes
as
$p
=>
$pipe
)
{
switch
(
$this
->
_descriptors
[
$p
][
1
])
{
case
'r'
:
\stream_set_blocking
(
$pipe
,
false
);
$_write
[]
=
$pipe
;
break
;
case
'w'
:
case
'a'
:
\stream_set_blocking
(
$pipe
,
true
);
$_read
[]
=
$pipe
;
break
;
}
}
while
(
true
)
{
foreach
(
$_read
as
$i
=>
$r
)
{
if
(
false
===
\is_resource
(
$r
))
{
unset
(
$_read
[
$i
]);
}
}
foreach
(
$_write
as
$i
=>
$w
)
{
if
(
false
===
\is_resource
(
$w
))
{
unset
(
$_write
[
$i
]);
}
}
foreach
(
$_except
as
$i
=>
$e
)
{
if
(
false
===
\is_resource
(
$e
))
{
unset
(
$_except
[
$i
]);
}
}
if
(
empty
(
$_read
)
&&
empty
(
$_write
)
&&
empty
(
$_except
))
{
break
;
}
$read
=
$_read
;
$write
=
$_write
;
$except
=
$_except
;
$select
=
\stream_select
(
$read
,
$write
,
$except
,
$this
->
getTimeout
());
if
(
0
===
$select
)
{
$this
->
getListener
()->
fire
(
'timeout'
,
new
EventBucket
());
break
;
}
foreach
(
$read
as
$i
=>
$_r
)
{
$pipe
=
\array_search
(
$_r
,
$this
->
_pipes
);
$line
=
$this
->
readLine
(
$pipe
);
if
(
false
===
$line
)
{
$result
=
[
false
];
}
else
{
$result
=
$this
->
getListener
()->
fire
(
'output'
,
new
EventBucket
([
'pipe'
=>
$pipe
,
'line'
=>
$line
,
])
);
}
if
(
true
===
\feof
(
$_r
)
||
\in_array
(
false
,
$result
,
true
))
{
\fclose
(
$_r
);
unset
(
$_read
[
$i
]);
break
;
}
}
foreach
(
$write
as
$j
=>
$_w
)
{
$result
=
$this
->
getListener
()->
fire
(
'input'
,
new
EventBucket
([
'pipe'
=>
\array_search
(
$_w
,
$this
->
_pipes
),
])
);
if
(
true
===
\feof
(
$_w
)
||
\in_array
(
false
,
$result
,
true
))
{
\fclose
(
$_w
);
unset
(
$_write
[
$j
]);
}
}
if
(
empty
(
$_read
))
{
break
;
}
}
$this
->
getListener
()->
fire
(
'stop'
,
new
EventBucket
());
return
;
}
/**
* Get pipe resource.
*/
protected
function
getPipe
(
int
$pipe
)
{
if
(!
isset
(
$this
->
_pipes
[
$pipe
]))
{
throw
new
ConsoleException
(
'Pipe descriptor %d does not exist, cannot read from it.'
,
2
,
$pipe
);
}
return
$this
->
_pipes
[
$pipe
];
}
/**
* Check if a pipe is seekable or not.
*/
protected
function
isPipeSeekable
(
int
$pipe
):
bool
{
if
(!
isset
(
$this
->
_seekable
[
$pipe
]))
{
$_pipe
=
$this
->
getPipe
(
$pipe
);
$data
=
\stream_get_meta_data
(
$_pipe
);
$this
->
_seekable
[
$pipe
]
=
$data
[
'seekable'
];
}
return
$this
->
_seekable
[
$pipe
];
}
/**
* Test for end-of-file.
*/
public
function
eof
(
int
$pipe
=
1
):
bool
{
return
\feof
(
$this
->
getPipe
(
$pipe
));
}
/**
* Read n characters.
*/
public
function
read
(
int
$length
,
int
$pipe
=
1
)
{
if
(
0
>
$length
)
{
throw
new
ConsoleException
(
'Length must be greater than 0, given %d.'
,
3
,
$length
);
}
return
\fread
(
$this
->
getPipe
(
$pipe
),
$length
);
}
/**
* Alias of $this->read().
*/
public
function
readString
(
int
$length
,
int
$pipe
=
1
)
{
return
$this
->
read
(
$length
,
$pipe
);
}
/**
* Read a character.
*/
public
function
readCharacter
(
int
$pipe
=
1
)
{
return
\fgetc
(
$this
->
getPipe
(
$pipe
));
}
/**
* Read a boolean.
*/
public
function
readBoolean
(
int
$pipe
=
1
)
{
return
(
bool
)
$this
->
read
(
1
,
$pipe
);
}
/**
* Read an integer.
*/
public
function
readInteger
(
int
$length
=
1
,
int
$pipe
=
1
)
{
return
(
int
)
$this
->
read
(
$length
,
$pipe
);
}
/**
* Read a float.
*/
public
function
readFloat
(
int
$length
=
1
,
int
$pipe
=
1
)
{
return
(
float
)
$this
->
read
(
$length
,
$pipe
);
}
/**
* Read an array.
* Alias of the $this->scanf() method.
*/
public
function
readArray
(?
string
$format
=
null
,
int
$pipe
=
1
)
{
return
$this
->
scanf
(
$format
,
$pipe
);
}
/**
* Read a line.
*/
public
function
readLine
(
int
$pipe
=
1
)
{
return
\stream_get_line
(
$this
->
getPipe
(
$pipe
),
1
<<
15
,
"
\n
"
);
}
/**
* Read all, i.e. read as much as possible.
*/
public
function
readAll
(
int
$offset
=
-
1
,
int
$pipe
=
1
)
{
$_pipe
=
$this
->
getPipe
(
$pipe
);
if
(
true
===
$this
->
isPipeSeekable
(
$pipe
))
{
$offset
+=
\ftell
(
$_pipe
);
}
else
{
$offset
=
-
1
;
}
return
\stream_get_contents
(
$_pipe
,
-
1
,
$offset
);
}
/**
* Parse input from a stream according to a format.
*/
public
function
scanf
(
string
$format
,
int
$pipe
=
1
):
array
{
return
\fscanf
(
$this
->
getPipe
(
$pipe
),
$format
);
}
/**
* Write n characters.
*/
public
function
write
(
string
$string
,
int
$length
,
int
$pipe
=
0
)
{
if
(
0
>
$length
)
{
throw
new
ConsoleException
(
'Length must be greater than 0, given %d.'
,
4
,
$length
);
}
return
\fwrite
(
$this
->
getPipe
(
$pipe
),
$string
,
$length
);
}
/**
* Write a string.
*/
public
function
writeString
(
string
$string
,
int
$pipe
=
0
)
{
$string
=
(
string
)
$string
;
return
$this
->
write
(
$string
,
\strlen
(
$string
),
$pipe
);
}
/**
* Write a character.
*/
public
function
writeCharacter
(
string
$char
,
int
$pipe
=
0
)
{
return
$this
->
write
((
string
)
$char
[
0
],
1
,
$pipe
);
}
/**
* Write a boolean.
*/
public
function
writeBoolean
(
bool
$boolean
,
int
$pipe
=
0
)
{
return
$this
->
write
((
string
)
(
bool
)
$boolean
,
1
,
$pipe
);
}
/**
* Write an integer.
*/
public
function
writeInteger
(
int
$integer
,
int
$pipe
=
0
)
{
$integer
=
(
string
)
(
int
)
$integer
;
return
$this
->
write
(
$integer
,
\strlen
(
$integer
),
$pipe
);
}
/**
* Write a float.
*/
public
function
writeFloat
(
float
$float
,
int
$pipe
=
0
)
{
$float
=
(
string
)
(
float
)
$float
;
return
$this
->
write
(
$float
,
\strlen
(
$float
),
$pipe
);
}
/**
* Write an array.
*/
public
function
writeArray
(
array
$array
,
int
$pipe
=
0
)
{
$array
=
\var_export
(
$array
,
true
);
return
$this
->
write
(
$array
,
\strlen
(
$array
),
$pipe
);
}
/**
* Write a line.
*/
public
function
writeLine
(
string
$line
,
int
$pipe
=
0
)
{
if
(
false
===
$n
=
\strpos
(
$line
,
"
\n
"
))
{
return
$this
->
write
(
$line
.
"
\n
"
,
\strlen
(
$line
)
+
1
,
$pipe
);
}
++
$n
;
return
$this
->
write
(
\substr
(
$line
,
0
,
$n
),
$n
,
$pipe
);
}
/**
* Write all, i.e. as much as possible.
*/
public
function
writeAll
(
string
$string
,
int
$pipe
=
0
)
{
return
$this
->
write
(
$string
,
\strlen
(
$string
),
$pipe
);
}
/**
* Truncate a file to a given length.
*/
public
function
truncate
(
int
$size
,
int
$pipe
=
0
):
bool
{
return
\ftruncate
(
$this
->
getPipe
(
$pipe
),
$size
);
}
/**
* Get filename component of path.
*/
public
function
getBasename
():
string
{
return
\basename
(
$this
->
getCommand
());
}
/**
* Get directory name component of path.
*/
public
function
getDirname
():
string
{
return
\dirname
(
$this
->
getCommand
());
}
/**
* Get status.
*/
public
function
getStatus
():
array
{
return
\proc_get_status
(
$this
->
getStream
());
}
/**
* Get exit code (alias of $this->getStatus()['exitcode']);.
*/
public
function
getExitCode
():
int
{
$handle
=
$this
->
getStatus
();
return
$handle
[
'exitcode'
];
}
/**
* Whether the processus have ended successfully.
*
* @return bool
*/
public
function
isSuccessful
():
bool
{
return
0
===
$this
->
getExitCode
();
}
/**
* Terminate the process.
*
* Valid signals are self::SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGKILL,
* SIGALRM and SIGTERM.
*/
public
function
terminate
(
int
$signal
=
self
::
SIGTERM
):
bool
{
return
\proc_terminate
(
$this
->
getStream
(),
$signal
);
}
/**
* Set command name.
*/
protected
function
setCommand
(
string
$command
)
{
$old
=
$this
->
_command
;
$this
->
_command
=
\escapeshellcmd
(
$command
);
return
$old
;
}
/**
* Get command name.
*/
public
function
getCommand
()
{
return
$this
->
_command
;
}
/**
* Set command options.
*/
protected
function
setOptions
(
array
$options
):
array
{
foreach
(
$options
as
&
$option
)
{
$option
=
\escapeshellarg
(
$option
);
}
$old
=
$this
->
_options
;
$this
->
_options
=
$options
;
return
$old
;
}
/**
* Get options.
*/
public
function
getOptions
():
array
{
return
$this
->
_options
;
}
/**
* Get command-line.
*/
public
function
getCommandLine
():
string
{
$out
=
$this
->
getCommand
();
foreach
(
$this
->
getOptions
()
as
$key
=>
$value
)
{
if
(!
\is_int
(
$key
))
{
$out
.=
' '
.
$key
.
'='
.
$value
;
}
else
{
$out
.=
' '
.
$value
;
}
}
return
$out
;
}
/**
* Set current working directory of the process.
*/
protected
function
setCwd
(
string
$cwd
)
{
$old
=
$this
->
_cwd
;
$this
->
_cwd
=
$cwd
;
return
$old
;
}
/**
* Get current working directory of the process.
*/
public
function
getCwd
():
string
{
return
$this
->
_cwd
;
}
/**
* Set environment of the process.
*/
protected
function
setEnvironment
(
array
$environment
)
{
$old
=
$this
->
_environment
;
$this
->
_environment
=
$environment
;
return
$old
;
}
/**
* Get environment of the process.
*/
public
function
getEnvironment
()
{
return
$this
->
_environment
;
}
/**
* Set timeout of the process.
*/
public
function
setTimeout
(
int
$timeout
)
{
$old
=
$this
->
_timeout
;
$this
->
_timeout
=
$timeout
;
return
$old
;
}
/**
* Get timeout of the process.
*/
public
function
getTimeout
():
int
{
return
$this
->
_timeout
;
}
/**
* Set process title.
*/
public
static
function
setTitle
(
string
$title
)
{
\cli_set_process_title
(
$title
);
}
/**
* Get process title.
*/
public
static
function
getTitle
()
{
return
\cli_get_process_title
();
}
/**
* Found the place of a binary.
*/
public
static
function
locate
(
string
$binary
)
{
if
(
isset
(
$_ENV
[
'PATH'
]))
{
$separator
=
':'
;
$path
=
&
$_ENV
[
'PATH'
];
}
elseif
(
isset
(
$_SERVER
[
'PATH'
]))
{
$separator
=
':'
;
$path
=
&
$_SERVER
[
'PATH'
];
}
elseif
(
isset
(
$_SERVER
[
'Path'
]))
{
$separator
=
';'
;
$path
=
&
$_SERVER
[
'Path'
];
}
else
{
return
null
;
}
foreach
(
\explode
(
$separator
,
$path
)
as
$directory
)
{
if
(
true
===
\file_exists
(
$out
=
$directory
.
\DIRECTORY_SEPARATOR
.
$binary
))
{
return
$out
;
}
}
return
null
;
}
/**
* Quick process execution.
* Returns only the STDOUT.
*/
public
static
function
execute
(
string
$commandLine
,
bool
$escape
=
true
):
string
{
if
(
true
===
$escape
)
{
$commandLine
=
\escapeshellcmd
(
$commandLine
);
}
return
\rtrim
(
\shell_exec
(
$commandLine
)
??
''
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, May 16, 21:32 (1 d, 9 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
07/bd/2baeb29d6116c413da7d8fc3c245
Default Alt Text
ConsoleProcessus.php (20 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment