Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
Attachment
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
6 / 6
25
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
1 / 1
20
 createFromFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createFromStream
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFilename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBasename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStream
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Mail\Message;
15
16/**
17 * Attachment message part class
18 *
19 * @category   Pop
20 * @package    Pop\Mail
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    4.0.0
25 */
26class Attachment extends AbstractPart
27{
28
29    /**
30     * File attachment
31     * @var ?string
32     */
33    protected ?string $filename = null;
34
35    /**
36     * File attachment basename
37     * @var ?string
38     */
39    protected ?string $basename = null;
40
41    /**
42     * File attachment original stream content
43     * @var ?string
44     */
45    protected ?string $stream = null;
46
47    /**
48     * Common content types for auto-detection
49     * @var array
50     */
51    protected array $contentTypes = [
52        'csv'    => 'text/csv',
53        'doc'    => 'application/msword',
54        'docx'   => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
55        'gif'    => 'image/gif',
56        'html'   => 'text/html',
57        'htm'    => 'text/html',
58        'jpe'    => 'image/jpeg',
59        'jpg'    => 'image/jpeg',
60        'jpeg'   => 'image/jpeg',
61        'log'    => 'text/plain',
62        'md'     => 'text/plain',
63        'pdf'    => 'application/pdf',
64        'png'    => 'image/png',
65        'ppt'    => 'application/vnd.ms-powerpoint',
66        'pptx'   => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
67        'svg'    => 'image/svg+xml',
68        'tif'    => 'image/tiff',
69        'tiff'   => 'image/tiff',
70        'tsv'    => 'text/tsv',
71        'txt'    => 'text/plain',
72        'xls'    => 'application/vnd.ms-excel',
73        'xlsx'   => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
74        'zip'    => 'application/x-zip'
75    ];
76
77    /**
78     * Constructor
79     *
80     * Instantiate the mail attachment object
81     *
82     * @param  ?string $file
83     * @param  ?string $stream
84     * @param  array   $options  ['contentType', 'basename', 'encoding', 'chunk']
85     * @throws Exception
86     */
87    public function __construct(?string $file = null, ?string $stream = null, array $options = [])
88    {
89        if ($stream !== null) {
90            $this->stream   = $stream;
91            $this->basename = $options['basename'] ?? 'file.tmp';
92        } else if ($file !== null) {
93            if (!file_exists($file)) {
94                throw new Exception("Error: The file '" . $file . "' does not exist.");
95            } else {
96                $this->filename = $file;
97                $this->stream   = file_get_contents($file);
98                $this->basename = basename($file);
99            }
100        }
101
102        $chunk       = (isset($options['chunk'])) ? (bool)$options['chunk'] : false;
103        $contentType = null;
104        $encoding    = null;
105
106        // Set encoding
107        if (isset($options['encoding'])) {
108            switch (strtoupper($options['encoding'])) {
109                case Attachment::BASE64:
110                case Attachment::QUOTED_PRINTABLE:
111                case Attachment::BINARY:
112                case Attachment::_8BIT:
113                case Attachment::_7BIT:
114                    $encoding = strtoupper($options['encoding']);
115            }
116        }
117
118        // Set content type
119        foreach ($options as $key => $value) {
120            $key = strtolower($key);
121            if (($key == 'content-type') || ($key == 'contenttype') ||
122                ($key == 'mime-type') || ($key == 'mimetype') || ($key == 'mime')) {
123                $contentType = $value;
124            }
125        }
126
127        // Fallback content type detection
128        if (($contentType === null) && (str_contains($this->basename, '.'))) {
129            $pathInfo    = pathinfo($this->basename);
130            $ext         = strtolower($pathInfo['extension']);
131            $contentType = (array_key_exists($ext, $this->contentTypes)) ?
132                $this->contentTypes[$ext] : 'application/octet-stream';
133        }
134
135        parent::__construct($this->stream, $contentType . '; name="' . $this->basename . '"', $encoding, $chunk);
136
137        $this->addHeader('Content-Description', $this->basename)
138             ->addHeader('Content-Disposition', 'attachment; filename="' . $this->basename . '"')
139             ->setCharSet(null);
140    }
141
142    /**
143     * Create attachment from file
144     *
145     * @param  string  $file
146     * @param  array   $options  ['contentType', 'basename', 'encoding', 'chunk']
147     * @return Attachment
148     */
149    public static function createFromFile(string $file, array $options = []): Attachment
150    {
151        return new Attachment($file, null, $options);
152    }
153
154    /**
155     * Create attachment from stream
156     *
157     * @param  string  $stream
158     * @param  array   $options  ['contentType', 'basename', 'encoding', 'chunk']
159     * @return Attachment
160     */
161    public static function createFromStream(string $stream, array $options = []): Attachment
162    {
163        return new Attachment(null, $stream, $options);
164    }
165
166    /**
167     * Get attachment filename
168     *
169     * @return ?string
170     */
171    public function getFilename(): ?string
172    {
173        return $this->filename;
174    }
175
176    /**
177     * Get attachment basename
178     *
179     * @return ?string
180     */
181    public function getBasename(): ?string
182    {
183        return $this->basename;
184    }
185
186    /**
187     * Get attachment original stream content
188     *
189     * @return ?string
190     */
191    public function getStream(): ?string
192    {
193        return $this->stream;
194    }
195
196}