A program is a text intended to express the computation of a result
that answers a question about the data. For example, a sorting program
will take a list of values (the data) and should compute a result
which is another list of the same values, but sorted according to some
comparison function.
This text is to be expressed formally in some kind of language that is
(or should be) precisely defined, both regarding what constitute a
legitimate program text, and how some computational meaning can be
associated to this text. Such a precise definition is often abstract
(possibly mathematical), and may ignore some concrete problems such as
computer limitations.
But then, the language is implemented so that programs can be
executed. There are again many ways of doing such an implementation,
using a intepreter of the original program text, or compiling to some
intermediate code (byte-code for example), then possibly interpreting
this intemediate code, or compiling it further into machine code. And
there could be other variations. And of course, there are many ways of
writing a compiler or an interpreter, and many machines to run them on.
Furthermore there may be several formal definitions, and several
implementations, hopefully consistent.
Errors can be classified in relation the structure of either or both a
formal definition or/and an implementation. However you could have strange
situations where errors are classified according to an old reference
implementation which is no longer the one being used.
This essentially means that classification of errors is a not really
a very stable topic. Furthermore, some languages may distinguish
several levels of errors depending on whether something is
definitely wrong (the program will not even run) or on whether you are
doing something not recommended, but that will still produce some
computation, that might make sense. This is even reflected in features
of programming languages such as exceptions, that may or may not be
recovered from.
Standard distinctions are:
Syntax errors: the text given does not conform the structure of a
program text, independently of what it should mean. This may refer
only to a formal language syntax, usually context-free. It may
sometimes go further and include the checking some basic features such
as variable declarations (if any) or type consistency, though these
may also be considered as semantic errors.
Semantic or logical errors: these are errors that can be detected when
actually running the program which are due to the fact that the
program runs into a calculation that does not make semantic sense,
such as dividing by zero, or indexing an array out-of bounds. Calling
a function with the wrong number of arguments, or with arguments of
the wrong type may also be considered a semantic error. In some
languages, errors may actually be user-defined by means of exceptions,
when they correspond to some user defined higher-level semantics for
part of his program (though there are other uses of exceptions). Some
of these errors are sometimes also called run-time errors as they are
detected at runtime, but they should not be confused with hardware
limitation errors.
Hardware limitation errors: these are errors due to the fact that the
implementation is on a real machine that has limitations. For example
this may be an integer too large to fit in a memory word, or lack of
sufficient memory to create a data structure. These also usually
detected at run-time.
Regarding semantic errors, and hardware limitation errors, it is
sometimes possible to detect them before executing the program, with
what is called static semantic analysis. This is often the case
for declarations or uninitialized variables, or for type errors, or
for division by zero and some array bound checking, but it may go much
further. Static semantic analysis is also important in compilers for
many optimisation techniques. There is often a separation between
static and dynamic semantics. The best definition I can imagine is
that static semantics concerns properties that are decidable at
compile time, without the actual data. So division by zero would not
be part of static semantics in general. This goes to say that some
dynamic semantic errors may still be sometimes detected at compile
time. The same goes for hardware limitation errors.
But every language designer or implementor has a right
to classify errors as he will, unless bound by a contract or license.
That may well be the case for your PHP example. And anyone may as well
make a distinction between semantic and logical errors, though I would
not know how to define a difference, unless possibly being told in detail about these errors. One could be used to denote error in intent (logical error) , and would not be detectable by the system.
Note that there may be other kinds of error in a program, that will
not usually be detected by the system. That includes in particular
inconsistencies of the program with its specification (or what I called error in intent: the user is not doing what he meant do do), or possibly
errors in the specification itself. There may also be errors due to
hardware limitations such as rounding errors when working with real
numbers.