Overview

Namespaces

  • Jlab
    • Eloglib

Classes

  • Jlab\Eloglib\Attachment
  • Jlab\Eloglib\FileAttachment
  • Jlab\Eloglib\Logentry
  • Jlab\Eloglib\LogentryUtil
  • Jlab\Eloglib\URLAttachment
  • Jlab\Eloglib\User

Exceptions

  • Jlab\Eloglib\InvalidXMLException
  • Jlab\Eloglib\IOException
  • Jlab\Eloglib\LogentryException
  • Jlab\Eloglib\LogRuntimeException
  • Jlab\Eloglib\ServerException
  • Jlab\Eloglib\UserException
  • Overview
  • Namespace
  • Class
  1: <?php
  2: /**
  3:  * Created by PhpStorm.
  4:  * User: theo
  5:  * Date: 9/7/17
  6:  * Time: 2:43 PM
  7:  */
  8: 
  9: namespace Jlab\Eloglib;
 10: 
 11: 
 12: use DOMDocument;
 13: use DOMXPath;
 14: use stdClass;
 15: 
 16: class LogentryUtil
 17: {
 18: 
 19:     /**
 20:      * Stores the most recently returned message from saveToServer.
 21:      * @var string
 22:      */
 23:     public static $lastServerMsg;
 24: 
 25: 
 26:     /**
 27:      * Saves a Logentry object to an XML file
 28:      *
 29:      * @param string $filename
 30:      * @param Logentry $entry
 31:      * @throws
 32:      */
 33:     public static function saveToFile($filename, Logentry $entry)
 34:     {
 35:         if (file_put_contents($filename, $entry->getXML()) === false) {
 36:             throw new IOException("Failed to write $filename");
 37:         }
 38:     }
 39: 
 40:     /**
 41:      * Saves a Logentry's XML output to a file in the elog queue directory
 42:      * Returns the name of the file that was saved
 43:      *
 44:      * @param Logentry $entry
 45:      * @param bool $validate whether to perform schema validation before queuing.
 46:      * @return string
 47:      * @throws IOException if unable to write queue file
 48:      * @throws InvalidXMLException
 49:      */
 50:     public static function saveToQueue(Logentry $entry, $validate = true)
 51:     {
 52:         if ($validate){
 53:             if (! self::isValidEntry($entry)){
 54:                 throw new InvalidXMLException("Schema validation of the entry fails: \n" . self::validationErrors());
 55:             }
 56:         }
 57: 
 58:         // Try, and if necessary, keep trying until we get a filename
 59:         // that doesn't already exist in the queue directory.
 60:         $filename = self::queuePath() . DIRECTORY_SEPARATOR . self::queueFileName();
 61:         while (file_exists($filename)) {
 62:             $filename = self::$queueDir . DIRECTORY_SEPARATOR . self::queueFileName();
 63:         }
 64: 
 65:         if (file_put_contents($filename, $entry->getXML()) === false) {
 66:             throw new IOException("Failed to save $filename");
 67:         }
 68: 
 69:         return $filename;
 70:     }
 71: 
 72: 
 73:     public static function queuePath()
 74:     {
 75:         return getenv('DEFAULT_UNIX_QUEUE_PATH');
 76:     }
 77: 
 78:     /**
 79:      * Returns a file name in the recommended format:
 80:      *    YYYYMMDD_HHMMSS_PID_HOSTNAME_RND.xml
 81:      * @return string
 82:      */
 83:     public static function queueFileName()
 84:     {
 85:         $pid = posix_getpid();
 86:         $hostname = trim(`hostname`);
 87:         $date = date('Ymd');
 88:         $time = date('His');
 89:         $name = sprintf("%s_%s_%s_%s_%d.xml", $date, $time, $pid, $hostname, rand(1, 999));
 90:         return $name;
 91:     }
 92: 
 93:     /**
 94:      * Saves a Logentry to the server via HTTPS.
 95:      *
 96:      * Returns boolean indicating sucess or failure.  The raw server response
 97:      * is stored in LogentryUtil::$lastServerMsg
 98:      *
 99:      * @param Logentry $entry
100:      * @return string
101:      * @throws
102:      */
103:     public static function saveToServer(Logentry $entry)
104:     {
105:         $xmlFile = self::tmpXMLFile($entry->getXML());
106:         //var_dump($xmlFile);
107:         //var_dump(self::submitUrl($xmlFile));
108: 
109:         $FH = fopen($xmlFile, 'r');
110:         $ch = curl_init();
111:         curl_setopt($ch, CURLOPT_URL, self::submitUrl($xmlFile));
112:         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);  // cert is self-signed right now
113:         curl_setopt($ch, CURLOPT_SSLCERT, self::certificateFile());
114:         curl_setopt($ch, CURLOPT_INFILE, $FH);
115:         curl_setopt($ch, CURLOPT_INFILESIZE, filesize($xmlFile));
116:         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
117:         curl_setopt($ch, CURLOPT_PUT, true);
118:         $result = curl_exec($ch);
119:         fclose($FH);
120:         //var_dump($result);
121:         if ($result === false) {
122:             throw new ServerException('curl failure.  Unable to send file.' . curl_error($ch));
123:         } else {
124:             self::$lastServerMsg = $result;
125:             $success = self::extractLognumber($result);
126:             unlink($xmlFile);
127:             return $success;
128:         }
129: 
130:     }
131: 
132:     protected static function tmpXMLFile($xml)
133:     {
134:         $basename = self::queueFileName();
135:         $filename = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $basename;
136: 
137:         while (file_exists($filename)) {
138:             $basename = self::queueFileName();
139:             $filename = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $basename;
140:         }
141:         if (file_put_contents($filename, $xml) === false) {
142:             throw new IOException("Unable to write XML temp file: " . $filename);
143:         }
144:         return $filename;
145:     }
146: 
147:     protected static function submitUrl($filename)
148:     {
149:         return getenv('SUBMIT_URL') . '/' . urlencode(basename($filename));
150:     }
151: 
152:     public static function certificateFile()
153:     {
154:         $filename = getenv('ELOGCERT_FILE');
155:         if (self::isSimpleFilename($filename)) {
156:             $filename = self::userHome() . DIRECTORY_SEPARATOR . $filename;
157:         }
158:         return $filename;
159:     }
160: 
161:     /**
162:      * Determines if the file name is simple (with no path component) or not.
163:      *
164:      * @param string $filename
165:      * @return bool
166:      */
167:     public static function isSimpleFilename($filename)
168:     {
169:         return $filename === basename($filename);
170:     }
171: 
172:     /**
173:      * Return the user's home directory.
174:      *
175:      * The code below was borrowed from https://github.com/drush-ops/drush
176:      *
177:      * @return mixed string or null
178:      */
179:     public static function userHome()
180:     {
181:         // getenv('HOME') isn't set on Windows and generates a Notice.
182:         $home = getenv('HOME');
183:         if (!empty($home)) {
184:             // home should never end with a trailing slash.
185:             $home = rtrim($home, '/');
186:         } elseif (!empty($_SERVER['HOMEDRIVE']) && !empty($_SERVER['HOMEPATH'])) {
187:             // home on windows
188:             $home = $_SERVER['HOMEDRIVE'] . $_SERVER['HOMEPATH'];
189:             // If HOMEPATH is a root directory the path can end with a slash. Make sure
190:             // that doesn't happen.
191:             $home = rtrim($home, '\\/');
192:         }
193:         return empty($home) ? null : $home;
194:     }
195: 
196:     /**
197:      * Extracts and returns the log number out of an XML-formatted server response.
198:      *
199:      * @internal
200:      * Example success response:
201:      *
202:      * <Response stat="ok">
203:      *   <msg>Entry saved.</msg>
204:      *   <lognumber>3484070</lognumber>
205:      *   <url>https://logbooks.jlab.org/entry/3484070</url>
206:      *  </Response>
207:      *
208:      * Example error response:
209:      *
210:      * <Response stat="fail">
211:      *   <msg>an error occurred...</msg>
212:      * </Response>
213:      *
214:      * @param string $text XML server response
215:      * @return integer
216:      * @throws ServerException
217:      */
218:     public static function extractLognumber($text)
219:     {
220:         $dom = new DOMDocument();
221:         if (!$dom->loadXML($text)) {
222:             throw new ServerException("Unable to process server response: \n" . $text);
223:         }
224: 
225:         $root = $dom->documentElement->tagName;
226:         if ($root == 'Response') {
227:             $stat = $dom->documentElement->getAttribute('stat');
228:             $xpath = new DOMXpath($dom);
229:             if ($stat == 'ok') {
230:                 return $xpath->query('lognumber')->item(0)->nodeValue;
231:             } elseif ($stat == 'fail') {
232:                 $msg = $xpath->query('msg')->item(0)->nodeValue;
233:                 throw new ServerException("Error response from Server: \n" . $msg);
234:             }
235:         }
236:         throw new ServerException("Server returned unexpected XML response:\n" . $text);
237:     }
238: 
239:     /**
240:      * Determines whether a Logentry object validates against the XML schema.
241:      *
242:      * If the optional schema parameter is not provided, the environment variable
243:      * LOG_ENTRY_SCHEMA_URL will be used.
244:      *
245:      * If the validation fails, return value will be false and the validationErrors() method
246:      * can be called in order to obtain the text of any error message(s).
247:      *
248:      * @param Logentry $entry
249:      * @param string $schema URL of XML schema to use for validation
250:      * @return boolean
251:      * @throws IOException if XML cannot be written to a text file.
252:      */
253:     public static function isValidEntry(Logentry $entry, $schema = null)
254:     {
255:         // We need to save the current entry to a temp file and then
256:         // load it as a DOM document.
257:         $filename = tempnam(sys_get_temp_dir(), 'validateXML_');
258:         if (file_put_contents($filename, $entry->getXML()) === false) {
259:             throw new IOException("Failed to write temp file $filename");
260:         }
261: 
262:         return self::isValidXMLFile($filename, $schema);
263:     }
264: 
265:     /**
266:      * Determines whether a Logentry XML file validates against the XML schema.
267:      *
268:      * If the optional schema parameter is not provided, the environment variable
269:      * LOG_ENTRY_SCHEMA_URL will be used.
270:      *
271:      * If the validation fails, return value will be false and the validationErrors() method
272:      * can be called in order to obtain the text of any error message(s).
273:      *
274:      * @param string $filename containing Logentry XML
275:      * @param string $schema URL of XML schema to use for validation
276:      * @return boolean
277:      * @throws IOException if XML file not readable
278:      */
279:     public static function isValidXMLFile($filename, $schema = null)
280:     {
281: 
282:         if ($schema == null) {
283:             $schema = getenv('LOG_ENTRY_SCHEMA_URL');
284:         }
285: 
286:         if (!is_readable($filename)) {
287:             throw new IOException("Unable to read $filename for validation");
288:         }
289: 
290:         libxml_use_internal_errors(true);
291: 
292:         $dom = new DOMDocument();
293:         $dom->load($filename, LIBXML_PARSEHUGE);   // inline base64 attachments can be large!
294: 
295:         return $dom->schemaValidate($schema);
296:     }
297: 
298:     /**
299:      * Returns the buffered error messages from the last XML
300:      * validation attempt and clears the buffer.
301:      *
302:      * @return string;
303:      */
304:     static public function validationErrors()
305:     {
306:         $return = '';
307:         $errors = libxml_get_errors();
308:         foreach ($errors as $error) {
309:             $return .= self::getXMLErrorString($error);
310:         }
311:         libxml_clear_errors();
312:         return $return;
313:     }
314: 
315:     /**
316:      * Converts a libXML error into human readable form.
317:      * @param $error a libXML error
318:      * @return $string
319:      */
320:     static private function getXMLErrorString($error)
321:     {
322:         $return = '';
323:         switch ($error->level) {
324:             case LIBXML_ERR_WARNING:
325:                 $return .= "Warning $error->code: ";
326:                 break;
327:             case LIBXML_ERR_ERROR:
328:                 $return .= "Error $error->code: ";
329:                 break;
330:             case LIBXML_ERR_FATAL:
331:                 $return .= "Fatal Error $error->code: ";
332:                 break;
333:         }
334:         $return .= trim($error->message) .
335:             "\n  Line: $error->line" .
336:             "\n  Column: $error->column";
337:         if ($error->file) {
338:             $return .= "\n  File: $error->file";
339:         }
340:         return "$return\n\n--------------------------------------------\n\n";
341:     }
342: 
343:     /**
344:      * returns an XML Response entity.
345:      *
346:      * The XML of the wrapped inside <Response> tags like so
347:      * <Response stat="ok">
348:      *   <lognumber>123456</lognumber>
349:      *   <url>https://logboks.jlab.org/entry/123456</url>
350:      *   <msg />
351:      * </Response>
352:      *
353:      * @param stdClass $O object containing data to return
354:      * @param string $stat status to return (ok|fail)
355:      */
356:     public static function getXMLresponse(stdClass $O, $stat = 'ok')
357:     {
358:         $xw = new xmlWriter();
359:         $xw->openMemory();
360:         $xw->startDocument();
361:         $xw->setIndent(true);
362:         $xw->startElement('Response');
363:         $xw->writeAttribute('stat', $stat);
364:         $xw->writeRaw("\n");
365:         foreach (get_object_vars($O) as $n => $var) {
366:             $xw->writeElement($n, htmlspecialchars($var));
367:         }
368:         $xw->endElement();
369:         $xw->endDocument();
370:         return $xw->outputMemory(true);
371:     }
372: 
373:     protected function hostOS()
374:     {
375: 
376:     }
377: 
378: }
API documentation generated by ApiGen