PHP Classes

File: src/Riverline/MultiPartParser/Part.php

Recommend this page to a friend!
  Classes of Melanie Wehowski   MIME Stub   src/Riverline/MultiPartParser/Part.php   Download  
File: src/Riverline/MultiPartParser/Part.php
Role: Class source
Content type: text/plain
Description: Class source
Class: MIME Stub
Generate PHP script that can self-execute the code
Author: By
Last change: Update of src/Riverline/MultiPartParser/Part.php
Date: 2 years ago
Size: 8,502 bytes
 

Contents

Class file image Download
<?php

namespace Riverline\MultiPartParser;

/**
 * Class Part
 * @package Riverline\MultiPartParser
 */
class Part
{
   
/**
     * @var array
     */
   
protected $headers;

   
/**
     * @var string
     */
   
protected $body;

   
/**
     * @var Part[]
     */
   
protected $parts = array();

   
/**
     * @var bool
     */
   
protected $multipart = false;

   
/**
     * MultiPart constructor.
     * @param string $content
     * @throws \InvalidArgumentException
     */
   
public function __construct($content)
    {
       
// Split headers and body
       
$splits = preg_split('/(\r?\n){2}/', $content, 2);

        if (
count($splits) < 2) {
            throw new \
InvalidArgumentException("Content is not valid, can't split headers and content");
        }

        list (
$headers, $body) = $splits;

       
// Regroup multiline headers
       
$currentHeader = '';
       
$headerLines = array();
        foreach (
preg_split('/\r?\n/', $headers) as $line) {
            if (empty(
$line)) {
                continue;
            }
            if (
preg_match('/^\h+(.+)/', $line, $matches)) {
               
// Multi line header
               
$currentHeader .= ' '.$matches[1];
            } else {
                if (!empty(
$currentHeader)) {
                   
$headerLines[] = $currentHeader;
                }
               
$currentHeader = trim($line);
            }
        }

        if (!empty(
$currentHeader)) {
           
$headerLines[] = $currentHeader;
        }

       
// Parse headers
       
$this->headers = array();
        foreach (
$headerLines as $line) {
           
$lineSplit = explode(':', $line, 2);
            if (
2 === count($lineSplit)) {
                list(
$key, $value) = $lineSplit;
               
// Decode value
               
$value = mb_decode_mimeheader(trim($value));
            } else {
               
// Bogus header
               
$key = $lineSplit[0];
               
$value = '';
            }
           
// Case-insensitive key
           
$key = strtolower($key);
            if (!isset(
$this->headers[$key])) {
               
$this->headers[$key] = $value;
            } else {
                if (!
is_array($this->headers[$key])) {
                   
$this->headers[$key] = (array)$this->headers[$key];
                }
               
$this->headers[$key][] = $value;
            }
        }

       
// Is MultiPart ?
       
$contentType = $this->getHeader('Content-Type');
        if (
'multipart' === strstr(self::getHeaderValue($contentType), '/', true)) {
           
// MultiPart !
           
$this->multipart = true;
           
$boundary = self::getHeaderOption($contentType, 'boundary');

            if (
null === $boundary) {
                throw new \
InvalidArgumentException("Can't find boundary in content type");
            }

           
$separator = '--'.preg_quote($boundary, '/');

           
// Get multi-part content
           
if (0 === preg_match('/'.$separator.'\r?\n(.+?)\r?\n'.$separator.'--/s', $body, $matches)) {
                throw new \
InvalidArgumentException("Can't find multi-part content");
            }

           
// Get parts
           
$parts = preg_split('/\r?\n'.$separator.'\r?\n/', $matches[1]);

            foreach (
$parts as $part) {
               
$this->parts[] = new self($part);
            }
        } else {
           
// Decode
           
$encoding = strtolower($this->getHeader('Content-Transfer-Encoding'));
            switch (
$encoding) {
                case
'base64':
                   
$body = base64_decode($body);
                    break;
                case
'quoted-printable':
                   
$body = quoted_printable_decode($body);
                    break;
            }

           
// Convert to UTF-8 ( Not if binary or 7bit ( aka Ascii ) )
           
if (!in_array($encoding, array('binary', '7bit'))) {
               
// Charset
               
$charset = self::getHeaderOption($contentType, 'charset');
                if (
null === $charset) {
                   
// Try to detect
                   
$charset = mb_detect_encoding($body) ?: 'utf-8';
                }

               
// Only convert if not UTF-8
               
if ('utf-8' !== strtolower($charset)) {
                   
$body = mb_convert_encoding($body, 'utf-8', $charset);
                }
            }

           
$this->body = $body;
        }
    }

   
/**
     * @return bool
     */
   
public function isMultiPart()
    {
        return
$this->multipart;
    }

   
/**
     * @return string
     * @throws \LogicException if is multipart
     */
   
public function getBody()
    {
        if (
$this->isMultiPart()) {
            throw new \
LogicException("MultiPart content, there aren't body");
        } else {
            return
$this->body;
        }
    }

   
/**
     * @return array
     */
   
public function getHeaders()
    {
        return
$this->headers;
    }

   
/**
     * @param string $key
     * @param mixed $default
     * @return mixed
     */
   
public function getHeader($key, $default = null)
    {
       
// Case-insensitive key
       
$key = strtolower($key);
        if (isset(
$this->headers[$key])) {
            return
$this->headers[$key];
        } else {
            return
$default;
        }
    }

   
/**
     * @param string $content
     * @return array
     */
   
static protected function parseHeaderContent($content)
    {
       
$parts = explode(';', $content);
       
$headerValue = array_shift($parts);
       
$options = array();
       
// Parse options
       
foreach ($parts as $part) {
            if (!empty(
$part)) {
               
$partSplit = explode('=', $part, 2);
                if (
2 === count($partSplit)) {
                    list (
$key, $value) = $partSplit;
                   
$options[trim($key)] = trim($value, ' "');
                } else {
                   
// Bogus option
                   
$options[$partSplit[0]] = '';
                }
            }
        }

        return array(
$headerValue, $options);
    }

   
/**
     * @param string $header
     * @return string
     */
   
static public function getHeaderValue($header)
    {
        list(
$value) = self::parseHeaderContent($header);

        return
$value;
    }

   
/**
     * @param string $header
     * @return string
     */
   
static public function getHeaderOptions($header)
    {
        list(,
$options) = self::parseHeaderContent($header);

        return
$options;
    }

   
/**
     * @param string $header
     * @param string $key
     * @param mixed $default
     * @return mixed
     */
   
static public function getHeaderOption($header, $key, $default = null)
    {
       
$options = self::getHeaderOptions($header);

        if (isset(
$options[$key])) {
            return
$options[$key];
        } else {
            return
$default;
        }
    }

   
/**
     * @return string
     */
   
public function getMimeType()
    {
       
// Find Content-Disposition
       
$contentType = $this->getHeader('Content-Type');

        return
self::getHeaderValue($contentType) ?: 'application/octet-stream';
    }

   
/**
     * @return string|null
     */
   
public function getName()
    {
       
// Find Content-Disposition
       
$contentDisposition = $this->getHeader('Content-Disposition');

        return
self::getHeaderOption($contentDisposition, 'name');
    }

   
/**
     * @return string|null
     */
   
public function getFileName()
    {
       
// Find Content-Disposition
       
$contentDisposition = $this->getHeader('Content-Disposition');

        return
self::getHeaderOption($contentDisposition, 'filename');
    }

   
/**
     * @return bool
     */
   
public function isFile()
    {
        return !
is_null($this->getFileName());
    }

   
/**
     * @return Part[]
     * @throws \LogicException if is not multipart
     */
   
public function getParts()
    {
        if (
$this->isMultiPart()) {
            return
$this->parts;
        } else {
            throw new \
LogicException("Not MultiPart content, there aren't any parts");
        }
    }

   
/**
     * @param string $name
     * @return Part[]
     * @throws \LogicException if is not multipart
     */
   
public function getPartsByName($name)
    {
       
$parts = array();

        foreach (
$this->getParts() as $part) {
            if (
$part->getName() === $name) {
               
$parts[] = $part;
            }
        }

        return
$parts;
    }
}