A symbol’s scope is the portion of the program where that symbol’s declaration is in effect, i.e. where that symbol is visible.

In phix, every variable must be declared before it is used. You can read a phix program from beginning to end without encountering any variables that have not been defined yet.

You can explicitly declare a forward routine that will be defined later, or just invoke it implicitly. Most of the builtin routines (excepting those with an include statement in the Definition section) are in fact effectively declared as explicit forward global routines, in psym.e/syminit().

Implicit forward routine calls are assumed to be local and generate a compilation error if still unresolved at the end of the file. If the routine is actually global (and not a typo) there must be an explicit forward global routine statement, and that must occur before the first (implicit) call. If the compiler can unambiguously resolve an implicit local routine to a global routine, it will, but at the same time also issue a warning.

Procedures, functions and types can call themselves recursively. Mutual recursion, where routine A calls routine B which directly or indirectly calls routine A, is fully supported.

It is also possible to call a routine that comes later in the source using the special functions routine_id(), and either call_func() or call_proc(), see Library Routines - Dynamic Calls . This mechanism was the only means of making a forward call in RDS Eu, and may therefore be quite prevalent in legacy code.

A symbol is defined from the point where it is declared to the end of its scope.
The scope of a variable declared inside a procedure or function (a parameter or private variable) ends at the end of the procedure or function.
The controlling variable used in a for-loop is special: if a variable of that name has not been declared in the immediately surrounding scope, it is automatically declared at the beginning of the loop, and its scope ends at the end of the for-loop. (Be warned there may be some cases where new/reuse is ill-defined/an implementation detail, eg nested functions.)
The scope of all other variables, constants, procedures, functions and types ends at the end of the source file in which they are declared and they are referred to as local, unless the keyword global precedes their declaration, in which case their scope extends indefinitely.

You can use the same name in many different for-loops as long as the loops are not nested. You do not have to declare loop variables as you would other variables. However should you chose to, you should use type integer, as phix does not allow floating point for loops.

When you include a phix file in a main file (see Special Top-Level Statements), only the variables and routines declared using the global keyword are accessible or even visible to the main file. The other, non-global, declarations in the included file are forgotten at the end of the included file, and you will get an error message, "not declared", if you try to use them in the main file.

The global keyword makes it clear which symbols can be used externally, and which symbols are strictly for internal use within the include file.
This information is helpful when maintaining or enhancing the file, or when learning how to use the file.

Compatibility Note: OpenEuphoria has private and export qualifiers as well as global. Phix simply treats all three identically, except for export in the top-level file when building a .dll or .so. The "priorities" handling as documented in psym.e addresses pretty much the same concerns but in a completely different manner.
Sometimes, when using include files developed by others, you will encounter a naming conflict. One of the include file authors has used the same name for a global symbol as one of the other authors. If you have the source, you can simply edit one of the include files to correct the problem, but then you would have repeat this process whenever a new version of the include file was released. Phix has a simpler way to solve this. Using an extension to the include statement, you can say for example:
     include johns_file.e as john
     include bills_file.e as bill
     john:x += 1
     bill:x += 2
In this case, the variable x was declared in two different files, and you want to refer to both variables in your file. Using the namespace identifier of either john or bill, you can attach a prefix to x to indicate which x you are referring to. We sometimes say that john refers to one namespace, while bill refers to another distinct namespace. You can attach a namespace identifier to any user-defined variable, constant, procedure or function. You can do it to solve a conflict, or simply to make things clearer. A namespace identifier has local scope. It is known only within the file that declares it, i.e. the file that contains the include statement. Different files might define different namespace identifiers to refer to the same included file. See also include.

Phix encourages you to restrict the scope of symbols. If all symbols were automatically global to the whole program, you might have a lot of naming conflicts, especially in a large program consisting of files written by many different programmers. A naming conflict might cause a compiler error message, or it could lead to a very subtle bug, where different parts of a program accidentally modify the same variable without being aware of it. Try to use the most restrictive scope that you can. Make variables private to one routine where possible, and where that is not possible, make them local to a file, rather than global to the whole program.

When phix looks up the declaration of a symbol, it first checks the current routine, then the current file, then globals in other files. At least that is the simple version, see the comments in psym.e for more detailed technical information. Symbols that are more localized will temporarily override symbols that are more globalized, without any error message occurring.

Constant declarations must be outside of any subroutine.
Constants can be global or local, but not private.

Historically, variable declarations inside a subroutine had to appear at the beginning, before the executable statements of the subroutine. However block scope is now supported, meaning a variable can be private to a single branch of an if-construct, for example, and they can now be mixed in with executable statements as needed.
Note however that block scopes may not "shadow" containing local scopes: attempts to declare another variable "i", for instance, when one is already visible from some containing block or routine scope will trigger a compilation error.

Likewise, declarations at the top level, outside of any subroutine, originally had to be at the very top, but can now be nested inside a loop or if-statement, and similarly cannot shadow or otherwise (temporarily) override any other locally defined containing block or file-level variables, however anything locally declared can shadow any (same-named) globals defined in another source file.