0

I have read a bunch of questions and answers (like this and this and even this which might be more relevant) on SO regarding this. They all deal with a file not being found due to a path incorrectly specified. In my case, the file is found and included (as you will see below), but the function is still not found. I get the error:

Fatal error: Call to undefined function mysender() in ....

Here is my first file (test.php) in which I am including another file (myemailsender.php below)

<?php
// File: test.php
namespace PHPMailer\PHPMailer;

error_reporting(E_ALL);
ini_set('display_errors', 1);

// I have tried all of the below methods
require_once('../files/myemailsender.php'); // I am pretty sure this works
// require_once('/var/www/example.com/files/mysender.php');
// require_once($_SERVER['DOCUMENT_ROOT'] . '/files/mysender.php'); // I can't use this because the file has to be executed as a Cron which doesn't have Server Variables.
// require_once('https://example.com/files/mysender.php'); // this gives a wrapper error

// Code to set the various variables goes here...
$SendParams = array("SendTo"=>$SendTo,
                "Body"=>$EmailMessage, 
                "Subject"=>$Subject);
mysender($SendParams);
?>

Here is the file myemailsender.php

<?php
// File: myemailsender.php
namespace PHPMailer\PHPMailer;

error_reporting(E_ALL);
ini_set('display_errors', 1);

echo "<br><br>DOCUMENT_ROOT: " . $_SERVER['DOCUMENT_ROOT']; // This prints!
require_once('../PHPMailer-master/src/PHPMailerAutoload.php');
require_once('../PHPMailer-master/src/PHPMailer.php');
require_once('../PHPMailer-master/src/SMTP.php');
require_once('../PHPMailer-master/src/Exception.php');

echo "<br><br>SERVER_NAME: " . $_SERVER['SERVER_NAME']; // This also prints!
// The below commented code works when this file is executed by itself in the browser
// $SendParams = array("SendTo"=>array("a@example.net,Me", "b@example.com, CS"), 
// "Body"=>"This is a test from SendEmail and contains only default text in the message body.", 
                // "Subject"=>"This is the default Subject");
// SendEmail($SendParams); This works if it is used in this file

function mysender($params) {
    $mail = new PHPMailer;
    $mail->isSMTP();
    $mail->SMTPAuth = true;
    $mail->SMTPSecure = 'tls';
    $mail->Host = "smtp.gmail.com";
    $mail->Mailer = "smtp";
    $mail->Port = 587;
    $mail->Username = "examplemail@gmail.com";
    $mail->Password = "xxxxxxxxxx";
    // removed the code which parses $params and creates the Body Subject etc.
    $mail->Body = $Body;
    $mail->Subject = $Subject;
    $mail->SetFrom('z@example.com', 'example');
    $mail->addReplyTo('y@example.com');
    $mail->addBCC('x@gmail.com');

    if (!$mail->send()) {
        echo "Mailer Error: " . $mail->ErrorInfo;
     } else {
        echo "Message sent!";
    }
}
echo "<br><br>REMOTE_ADDR: " . $_SERVER['REMOTE_ADDR']; //So does this!
?>

As I have noted, those messages print, so AFAICT the file is being loaded. I removed the entire code inside the function to make sure that the problem wasn't caused by bad code. Then, I started removing other pieces of code and found one that worked! I removed the line:

namespace PHPMailer\PHPMailer;

and the error went away! Of course, that makes the whole thing useless. Am I doing something wrong?

Incidentally, I did another test. I used the following code:

<?php
function consolelog($data) {
    if (is_array($data) || is_object($data)) {
        echo "<script>console.log('" . json_encode($data) . "');</script>";

    } else {
        echo "<script>console.log('". $data . "');</script>";
    }
}
?>

and included it in the same file (test.php above), like this:

require_once('../files/consolelog.php');
consolelog("It is working!");

The above works - the message appears in the page Console. So circling back, it appears that namespace line is the culprit. Can it be? How can I fix it?

Chiwda
  • 1,233
  • 7
  • 30
  • 52
  • `Of course, that makes the whole thing useless. `....why would it? Your custom code doesn't need to be part of the PHPMailer namespace. That's for the PHPMailer library, really. What actual problem occurs if you remove the namespace lines? – ADyson Jun 09 '22 at 10:45
  • @ADyson if I remove the namespace, I get the error "Fatal error: Class 'SMTP' not found in ..." – Chiwda Jun 09 '22 at 10:54
  • Well, that might be to do with ordering. If you look at the PHPMailer usage example in the docs (https://github.com/PHPMailer/PHPMailer#installation--loading) you'll notice that the `use` statement comes before the `require` statements, whereas you've got it the other way round. That might make a difference, I think. Also there is more than one `use` specified in the example. Not really sure why you've deviated from that. – ADyson Jun 09 '22 at 10:59
  • Apologies for that. I have fixed it (I miscopied from my editor to SO) and this is the way it is in my code - with the namespace as the first line. In fact, it complains if the namespace line is not the first line. – Chiwda Jun 09 '22 at 11:19
  • 1
    That's not what I meant. I'm talking about the `use` statement(s). You should be putting the `use` statement first (in myemailsender.php) as per the PHPMailer examples in the documentation. The `namespace` statement should be unnecessary (in both files) - your code is not part of PHPMailer. You're _using_ PHPMailer, not adding things to the library. I did not suggest removing the `use` statement! Again, I'm not really sure why you're deviating from the documentation. – ADyson Jun 09 '22 at 11:20
  • That's how it is shown on the GitHub page - https://github.com/PHPMailer/PHPMailer (under Installation & loading). Note that I am not using Composer. – Chiwda Jun 09 '22 at 11:23
  • No it's not. Look again more carefully. Your code is not the same as the example. Do you understand the difference between `use` and `namespace`? – ADyson Jun 09 '22 at 11:23
  • OK. Let me experiment. I suppose I am confused by Namespace and I am using both rather than one or the other? Is there a good explanation I can read to understand? – Chiwda Jun 09 '22 at 11:27
  • 1
    Well, `namespace` tries to make the code following it belong to the specified namespace. `use` merely tells the code how to access a namespace defined elsewhere. To make use of PHPMailer functionality, you merely need to `use` the namespace, as per the PHPMailer documentation example. You'd only put `namespace` if you wanted to make a new namespace, or extend an existing one with more code. You don't need or want to do that in order to use PHPMailer, and I'm pretty sure it's the cause of your "undefined" function error. – ADyson Jun 09 '22 at 11:34
  • 1
    You might want to also read the namespaces documentation in the PHP manual - https://www.php.net/manual/en/language.namespaces.basics.php . No doubt you can google other tutorials from other sources too. – ADyson Jun 09 '22 at 11:34

1 Answers1

2

It's all about the namespace. As @ADyson said, you don't need to use PHPMailer's namespace.

If you do this:

namespace PHPMailer\PHPMailer;
function mysender($params)...

the function you have defined is called (its fully-qualified function name, FQFN) \PHPMailer\PHPMailer\mysender(), not just \mysender(), and so calling it from outside that namespace will not work. To call that function you can do one of 4 things:

  • Declare the same namespace in the calling file
  • Add use PHPMailer\PHPMailer\mysender; to the calling file
  • Use the FQFN to call the function: \PHPMailer\PHPMailer\mysender($SendParams);
  • Don't namespace the function definition, so that it remains in the global scope

These name resolution rules are well documented. That said, your example shows you doing the first of these options, so I'm unsure why that isn't working for you, however, the right approach here is the last option – remove the namespace declaration from both your files; you just don't need it.

Separately, not using composer is a great way to make lots of unnecessary work for yourself. It's a really good system, definitely worth using for even the most trivial things.

Synchro
  • 35,538
  • 15
  • 81
  • 104