Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • None
  • system
    • base
    • caching
    • console
    • db
      • ar
      • schema
    • validators
    • web
      • actions
      • auth
      • helpers
      • widgets
        • captcha
        • pagers
  • zii
    • widgets
      • grid

Classes

  • AppFileUtil
  • CommandUtil
  • CrontabUtil
  • EncryptUtil
  • FileUtil
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: 
  3: /*****************************************************************************************
  4:  * X2Engine Open Source Edition is a customer relationship management program developed by
  5:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
  6:  * 
  7:  * This program is free software; you can redistribute it and/or modify it under
  8:  * the terms of the GNU Affero General Public License version 3 as published by the
  9:  * Free Software Foundation with the addition of the following permission added
 10:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 11:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
 12:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 13:  * 
 14:  * This program is distributed in the hope that it will be useful, but WITHOUT
 15:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 16:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 17:  * details.
 18:  * 
 19:  * You should have received a copy of the GNU Affero General Public License along with
 20:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
 21:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 22:  * 02110-1301 USA.
 23:  * 
 24:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
 25:  * California 95067, USA. or at email address contact@x2engine.com.
 26:  * 
 27:  * The interactive user interfaces in modified source and object code versions
 28:  * of this program must display Appropriate Legal Notices, as required under
 29:  * Section 5 of the GNU Affero General Public License version 3.
 30:  * 
 31:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 32:  * these Appropriate Legal Notices must retain the display of the "Powered by
 33:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
 34:  * technical reasons, the Appropriate Legal Notices must display the words
 35:  * "Powered by X2Engine".
 36:  *****************************************************************************************/
 37: 
 38: /**
 39:  * Stand-alone class for running more advanced command line programs.
 40:  *
 41:  * Provides a wrapper/shortcut object for running commands using proc_open.
 42:  * 
 43:  * This should ideally permit chaining of programs via pipes. An example, on
 44:  * Unix-like systems:
 45:  *
 46:  * $cmd->run('ps aux')->pipeTo('awk \'/foo/ {print $2}\'')->pipeTo('kill')->complete();
 47:  *
 48:  * The above would send SIGTERM to all processes matching "foo". However, this
 49:  * is not yet possible due to a "bug" in PHP:
 50:  * http://stackoverflow.com/questions/6014761/proper-shell-execution-in-php
 51:  *
 52:  * @package application.components.util
 53:  * @author Demitri Morgan <demitri@x2engine.com>
 54:  */
 55: class CommandUtil {
 56: 
 57:     const DEBUG = 0;
 58: 
 59:     /**
 60:      * Subprocess commands
 61:      * @var array
 62:      */
 63:     public $cmd = array();
 64: 
 65:     /**
 66:      * Saves input given to each program, for debugging/examination purposes
 67:      * @var type
 68:      */
 69:     public $inputs = array();
 70: 
 71:     /**
 72:      * The status of the last process that was completed.
 73:      */
 74:     public $lastReturnCode;
 75: 
 76:     /**
 77:      * @var string Operating system; a keyword "posix" for posix-compliant
 78:      *  operating systems like Linux/Unix, or "dos" for Windows-based systems.
 79:      */
 80:     public $os;
 81: 
 82:     /**
 83:      * The stack of subprocesses inputs/outputs currently executing.
 84:      *
 85:      * Each value corresponds to an index in {@link processes}.
 86:      * @var array
 87:      */
 88:     public $pipeline = array();
 89: 
 90:     public $procArrays = array(
 91:         'cmd',
 92:         'inputs',
 93:         'pipeline',
 94:         'processes',
 95:     );
 96: 
 97:     /**
 98:      * All subprocess handles of the current program, 
 99:      * @var array
100:      */
101:     public $processes = array();
102: 
103:     /**
104:      * Gathers some information about the operating environment.
105:      */
106:     public function __construct(){
107:         if(!function_exists('proc_open'))
108:             throw new Exception('The function "proc_open" does not exist or is not available on this system, so running command line programs will not work.');
109:         $this->os = substr(strtoupper(PHP_OS), 0, 3) == 'WIN' ? 'dos' : 'posix';
110:     }
111: 
112:     /**
113:      * Closes a process and throws an exception if it exited with error code.
114:      * @param type $ind
115:      * @throws Exception
116:      */
117:     public function close($ind){
118:         if(gettype($this->processes[$ind]) != 'resource')
119:             return;
120:         $this->debug('Closing process at index '.$ind);
121:         $err = stream_get_contents($this->pipeline[$ind][2]);
122:         if($code = proc_close($this->processes[$ind]) == -1 && self::DEBUG)
123:             throw new Exception("Command {$this->cmd[$ind]} exited with error status.".(empty($err) ? '' : " Error output was as follows: \n $err"));
124:         $this->debug('Closed process at index '.$ind);
125:         return $code;
126:     }
127: 
128:     /**
129:      * Returns true or false based on whether the named command exists on the system.
130:      * @param string $cmd Name of the command
131:      * @return bool
132:      */
133:     public function cmdExists($cmd){
134:         if($this->os == 'posix'){
135:             return trim($this->run("which $cmd")->output()) != null;
136:         }
137:     }
138: 
139:     /**
140:      * Closes all processes and returns the return value of proc_close from the last one.
141:      */
142:     public function complete(){
143:         $n_proc = $this->nProc();
144:         $code = 0;
145:         if($n_proc > 0){ // Close processes
146:             foreach($this->processes as $ind => $process){
147:                 $codeTmp = $this->close($ind);
148:                 if($ind == $n_proc - 1)
149:                     $code = $codeTmp;
150:             }
151:             // Empty arrays
152:             foreach($this->procArrays as $array)
153:                 $this->$array = array();
154:         }
155:         return $this->lastReturnCode = $code;
156:     }
157: 
158:     public function debug($msg,$lvl=1) {
159:         if(self::DEBUG >= $lvl) {
160:             echo "[debug] $msg\n";
161:         }
162:     }
163: 
164:     /**
165:      * Returns the current number of subprocesses.
166:      * @return integer
167:      */
168:     public function nProc(){
169:         return count($this->processes);
170:     }
171: 
172:     /**
173:      * Returns the output of the last command and closes/clears all processes.
174:      * @return string
175:      */
176:     public function output(){
177:         $n_proc = $this->nProc();
178:         if($n_proc > 0){
179:             $output = stream_get_contents($this->pipeline[$n_proc - 1][1]);
180:             $this->complete();
181:             return $output;
182:         } else
183:             return null;
184:     }
185:     
186:     /**
187:      * Wrapper for {@link pipeTo}
188:      * @param type $filter PCRE regular expression
189:      * @param type $cmd
190:      * @param type $cwd
191:      * @return CommandUtil
192:      */
193:     public function pipeFilteredTo($filter, $cmd, $cwd = null){
194:         return $this->pipeTo($cmd, $cwd, $filter);
195:     }
196: 
197:     /**
198:      * Takes the output of the last comand and pipes it to a new command
199:      * @param string $cmd
200:      * @param string $cwd
201:      * @param string $filter Optional regular expressions filter to restrict the input to only certain lines.
202:      * @return CommandUtil
203:      */
204:     public function pipeTo($cmd, $cwd = null, $filter = null){
205:         $n_proc = $this->nProc();
206:         $this->debug('pipeTo('.$cmd.'): $n_proc = '.$n_proc);
207:         if($n_proc == 0)
208:             throw new Exception('Cannot pipe to subprocess; no prior processes from which to pipe have been opened.');
209:         return $this->run($cmd, $n_proc - 1, $cwd, $filter);
210:     }
211: 
212:     /**
213:      * Runs a command on the command line.
214:      * @param string $cmd
215:      * @param resource|string $input The input for the program.
216:      * @param string $cwd The directory to work in while executing.
217:      * @return CommandUtil
218:      */
219:     public function run($cmd, $input = null, $cwd = null, $filter = null){
220:         $cwd = $cwd === null ? __DIR__ : $cwd;
221:         $descriptor = array(
222:             1 => array('pipe', 'w'),
223:             2 => array('pipe', 'w'),
224:         );
225:         // Read input
226:         $inputType = gettype($input);
227:         if($input !== null){
228:             if($inputType == 'resource'){
229:                 // Interpret as a file descriptor.
230:                 $inputText = stream_get_contents($input);
231:                 $this->debug("Interpreted input as a stream resource, and read input:\n$inputText",2);
232:             }else if($inputType == 'string'){
233:                 // Interpret as literal input
234:                 $inputText = $input;
235:                 $this->debug("Interpreted input as a string, and it is:\n$inputText",2);
236:             }else if($inputType == 'integer'){
237:                 // Interpret as an index of a process whose output will be used as input
238:                 $inputText = stream_get_contents($this->pipeline[$input][1]);
239:                 $this->close($input);
240:             }
241:             $descriptor[0] = array('pipe', 'r');
242:         }
243: 
244:         // Spawn new process
245:         $procIndex = $this->nProc();
246:         $this->debug("Spawning $cmd and storing process handle at index $procIndex");
247:         $this->cmd[$procIndex] = $cmd;
248:         $this->processes[$procIndex] = proc_open($cmd, $descriptor, $this->pipeline[$procIndex], $cwd);
249:         $filter = empty($filter) ? '/.*/' : $filter;
250:         
251:         // Write input to process
252:         if(!empty($inputText)){ // Send input to the program
253:             $this->debug("Writing to input of child process $procIndex...");
254:             $this->inputs[$procIndex] = $inputText;
255:             foreach(explode("\n", $inputText) as $inputLine)
256:                 if(preg_match($filter, $inputLine))
257:                     fwrite($this->pipeline[$procIndex][0], $inputLine);
258:             $this->debug("...done.");
259:         }
260:         return $this;
261:     }
262: 
263:     //////////////////////////
264:     // Cron-related methods //
265:     //////////////////////////
266:     //
267:     // The following methods are used for creating scheduled commands. Typically
268:     // these will only work in Linux/Unix environments.
269: 
270:     /**
271:      * Loads and returns the cron table. Performs environment check first.
272:      * @return string
273:      */
274:     public function loadCrontab() {
275:         // Check to see if everything is as it should be
276:         if(!$this->cmdExists('crontab'))
277:             throw new Exception('The "crontab" command does not exist on this system, so there is no way to set up cron jobs.',1);
278:         if($this->run('crontab -l')->complete() == -1)
279:             throw new Exception('There is a cron service available on this system, but PHP is running as a system user that does not have permission to use it.',2);
280:         // Get the existing crontab
281:         return $this->run('crontab -l')->output();
282:     }
283: 
284:     /**
285:      * Saves the cron table.
286:      *
287:      * Save the table to a temporary file, sends it to cron, and deletes the
288:      * temporary file.
289:      *
290:      * @param string $crontab The new contents of the updated cron table
291:      */
292:     public function saveCrontab($crontab) {
293:         $tempFile = __DIR__.DIRECTORY_SEPARATOR.'crontab-'.time();
294:         file_put_contents($tempFile, $crontab);
295:         $status = $this->run("crontab $tempFile")->complete();
296:         unlink($tempFile);
297:     }
298: }
299: 
300: ?>
301: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0