Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
Attachment
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
4 / 4
5
100.00% covered (success)
100.00%
1 / 1
 create
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 createFromContent
100.00% covered (success)
100.00%
3 / 3
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
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12namespace Pop\Mail\Message;
13
14use Pop\Mime\Part;
15
16/**
17 * Attachment message part class
18 *
19 * @category   Pop
20 * @package    Pop\Mail
21 * @author     Nick Sagona, III <nick@popphp.org>
22 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
23 * @license    https://www.popphp.org/license     New BSD License
24 * @version    5.0.0
25 */
26class Attachment extends Part
27{
28    use CharsetAwareTrait;
29    use PartContentTrait;
30
31    /**
32     * The original on-disk path this attachment was created from, if any.
33     * Deliberately diverges from parent::getFilename() (which returns the
34     * basename, decoded from headers) - three transports need the real
35     * openable path for curl_file_create().
36     * @var ?string
37     */
38    protected ?string $sourcePath = null;
39
40    /**
41     * Create an attachment from a file on disk
42     *
43     * @param  string             $file
44     * @param  ?string            $contentType
45     * @param  string             $disposition
46     * @param  Part\Body\Encoding $encoding
47     * @param  int|bool           $split
48     * @throws Exception
49     * @return static
50     */
51    public static function create(
52        string $file, ?string $contentType = null, string $disposition = 'attachment',
53        Part\Body\Encoding $encoding = Part\Body\Encoding::BASE64, int|bool $split = true
54    ): static
55    {
56        if (!file_exists($file)) {
57            throw new Exception("Error: The file '" . $file . "' does not exist.");
58        }
59        $attachment = parent::attachment($file, $contentType, $disposition, $encoding, $split);
60        $attachment->sourcePath = $file;
61        $attachment->addHeader('Content-Description', basename($file));
62        return $attachment;
63    }
64
65    /**
66     * Create an attachment from in-memory content
67     *
68     * @param  string             $content
69     * @param  string             $filename
70     * @param  ?string            $contentType
71     * @param  string             $disposition
72     * @param  Part\Body\Encoding $encoding
73     * @param  int|bool           $split
74     * @return static
75     */
76    public static function createFromContent(
77        string $content, string $filename, ?string $contentType = null, string $disposition = 'attachment',
78        Part\Body\Encoding $encoding = Part\Body\Encoding::BASE64, int|bool $split = true
79    ): static
80    {
81        $attachment = parent::attachmentFromContent($content, $filename, $contentType, $disposition, $encoding, $split);
82        $attachment->addHeader('Content-Description', basename($filename));
83        return $attachment;
84    }
85
86    /**
87     * Get the real on-disk path this attachment was created from.
88     *
89     * No fallback to parent::getFilename() is used deliberately. The parent method
90     * returns a basename decoded from headers; Part::attachmentFromContent() marks
91     * the body as file-like and unconditionally sets a Content-Disposition filename=
92     * parameter. A fallback would return a fabricated basename (e.g. 'generated.txt')
93     * instead of null for in-memory attachments created via createFromContent().
94     * This method guarantees null for in-memory attachments to honor that contract.
95     *
96     * @return ?string The real path if created from a file, null if in-memory
97     */
98    public function getFilename(): ?string
99    {
100        return $this->sourcePath;
101    }
102
103    /**
104     * Get the basename decoded from headers (the parent's original getFilename() behavior)
105     *
106     * @return ?string
107     */
108    public function getBasename(): ?string
109    {
110        return parent::getFilename();
111    }
112
113}