![]() |
12. External Files
Introduction
A script can execute another script using <MvDO>. It can also call a specific function in another file. This provides the opportunity of creating library files from which you can call specific functions. <MvDO> can also be used to include an entire file in the output, such as for server-side include, where
<MvDO>
<MvDO> works in two somewhat different ways, depending on whether NAME and VALUE are present. If they are, then the specified function is the only code in the external file that gets executed. Any other Miva Script and HTML code is ignored. If NAME and VALUE are omitted, everything in the external file is executed, and the results of all <MvASSIGN> tags are available to subsequent code in the current file; however, any function definitions in the external file are not available after the <MvDO> has been processed.
All system and global variables, and all open databases, are available to the code in the external file.
- <MvDO
- FILE="filename"
- NAME="var"
- VALUE="{func(args)}">
A value for FILE is required. If NAME and VALUE are specified, the function func defined in the file filename is run with arguments args and the result is assigned to the variable var. If NAME and VALUE are omitted, the file filename is processed as if it were included in the current file. In each case, any output from the external file is merged into the current file. filename can be a path: if it is a relative path, it is interpreted as relative to the current file; if it starts with a forward slash (/), it is relative to the Miva Script document root directory. <MvDO> is an empty tag.
Note: If the required attribute FILE is missing, a value (error message) for the variable mvdo_error is set, and the script continues processing.
<MvDO> tags can be nested in the sense that the file filename specified by the <MvDO> can itself contain an <MvDO>. The filename path specified by the "nested" <MvDO> is relative to the location of the file containing that <MvDO> (unless it starts with a forward slash, it which case it is relative to the Miva Script document root directory).
Note: There is a limit on the number of nested MvDO calls at run-time. This limit is fixed at 23 for Miva Merchant Mia; the default limit for Miva Merchant Empresa is 23 by default, but this can be changed by the server administrator via the maxfunctiondepth setting in the global configuration file.
Miva does not interpret server-side includes (SSI). <MvDO> can simulate the effect of an SSI of the type used to include another document in the current document. For example:
- <!--#include file="external.html"-->
- <MvDO FILE="external.html">
- <!--#include virtual="/external.html"-->
- <MvDO FILE="/external.html">
Multiple Passes Through a Program
If your program reads user input, you will have to re-submit the document to the browser in order for this data to be processed by the program. In the section on "Calling Functions" you saw how this can be done by specifying a function call in a form action. Another technique is to test the value of a variable passed from the form. Here is an example:
- <body>
- <h1>Say anything</h1>
- <MvIF EXPR = "{ speak EQ '' }">
- <FORM METHOD="POST">
- <p><b>I say: <input type="text" name="speak"></b></p>
- <input type="submit" name="foo" value="Send">
- </FORM>
- <MvELSE>
- <p><b>You said: <MvEVAL EXPR="{speak}"></b></p>
- </MvIF>
- </body>
In this example the variable speak controls the flow of the program. The first time the document is opened, speak will have no value, so the condition '{speak EQ ' '}' will be true and the following form will be displayed:
If the user enters 'My word' into the text box (which has the name speak) this text will be assigned to the variable speak when the form is submitted. On the next pass through the program, the condition '{speak EQ ' '}' will be false, and the second branch of the <MvIF> will be executed, displaying:
- You said: My word
The METHOD attribute of FORM must have the value POST. Since you are not calling a specific function, it is not necessary in this case to specify an ACTION for the FORM.
Using External Data Files
Data files (external input and output files, database files, database indexes) used by Miva Script programs must be stored in specific locations.
For Miva Merchant Empresa these locations are set by the Internet Host Provider. In Windows, the Miva Engine tab shows the data directory location. For Miva Merchant Mia, the WWW tab shows the data folder. For information about installation and configuration of Miva Merchant Empresa and Miva Merchant Mia, refer to the documentation at http://www.miva.com/docs.
There is no limit on the length of a filename that can be used by Miva Script, except for any limit that may be imposed by the operating system.
Miva Script provides tags that enable programs to read from and write to external text data files. Any external files that you read or write must be located in your data directory. Data files can have any file extension, though.dat is the most common.
Reading from External Files
<MvIMPORT>
<MvIMPORT> imports a record (a single line) of data fields, separated by the character(s) chars, from the file filename, and stores the data in the var1, var2, var3,... variables. FILE, FIELDS, and DELIMITER are required. Optionally, a condition can be specified with FILTER, so that records that do not match the condition will be discarded.
Also, optionally, the FILTER_TYPE attribute determines how the FILTER attribute behaves, as a variable or an expression. The default is expression.
The data can be processed with the code between the <MvIMPORT> and </MvIMPORT> tags. <MvIMPORT> will loop through the entire file, stopping when the end of the file is reached or an <MvIMPORTSTOP> is executed. The variable recno is automatically created, and contains the current record number in the input file, starting at 1. <MvIMPORT> tags can be nested to read data from multiple files within the same block of code. Records in data files can be terminated by line feeds, carriage returns, or line feed/carriage return pairs. Data files should end with a blank line. <MvIMPORTSTOP> is an empty tag.
Tip: Since searching in a text file is much slower than searching in a database, we recommend that if you will be reusing the data in a text file, you import it, store it in a Miva database, and perform searches on the database. This will speed up your scripts and avoid overloading your server.
In addition to taking input from users via forms, programs can read from data files. Suppose you have a data file called movies.dat that is in the following format:
Cape Fear|Martin Scorsese|1991
The Trouble With Harry|Alfred Hitchcock|1955
Dr Strangelove|Stanley Kubrick|1963
Strange Days|Kathryn Bigelow|1995
This is a simple text file in which each line (sometimes called a record) contains a film title, the director, and the year of release. Each piece of data is called a field; the fields on each line are separated by delimiters--in this example the delimiter is the '|' (vertical bar) character.
The <MvIMPORT> tag can be used to read in this file, line by line, and process the data in some way--for example, displaying it in a table.
- <TABLE>
- <TR><TH></TH>
- <TH>Title</TH>
- <TH>Director</TH>
- <TH>Year</TH></TR>
- <MvIMPORT
- FILE="movies.dat"
- FIELDS="title,director,year"
- DELIMITER="|">
- <TR><TD><MvEVAL EXPR="{recno}">.</TD>
- <TD><MvEVAL EXPR="{title}"></TD>
- <TD><MvEVAL EXPR="{director}"></TD>
- <TD><MvEVAL EXPR="{year}"></TD>
- </TR>
- </MvIMPORT>
- </TABLE>
This code will generate a table, row by row, and fill each row with data from a line of the data file.
There are certain parts of the table code that should appear only once, so these are placed outside the <MvIMPORT>...</MvIMPORT> loop. The <TABLE> and </TABLE> tags, and the first table row (<TR>), which displays the table header, are in this category.
The attributes of the <MvIMPORT> tag tell it to do the following:
FILE="movies.dat": Read a line of data from the file movies.dat.
DELIMITER="|": Assume that the fields in each line are separated by the character '|' (vertical bar).
FIELDS="title,director,year": Save the first three fields of data in the variables title, director, and year, respectively. (If there are more fields in the line than there are variables specified, the extra fields are just discarded.)
If this code were reading from the sample data shown above, the variables would have the following values after the first line of data was read:
- title = Cape Fear
- director = Martin Scorsese
- year = 1991
The next line of code inserts these values into a table row by displaying the values of the variables title, director, and year. The automatically-created variable recno will contain the current record (line) number, 1. The table row produced by the resulting HTML code will look something like this:
1. Cape Fear Martin Scorsese 1991Since the <MvIMPORT>...</MvIMPORT> block is, in effect, a loop, it will repeat this process for every line of data in the file, stopping when it reads the last line of data. The final table will look like this:
Filtering the Input
Sometimes you might be interested only in input data that satisfies a particular condition. In the previous example, you might be interested only in movies from 1995. Using the FILTER attribute, you can specify a condition that the input data must meet in order for <MvIMPORT> to pass it on to the program. You could modify the previous <MvIMPORT> as follows:
When <MvIMPORT> reads a line of data, it will now check that the value assigned to the variable year equals '1995'. If it doesn't, the data is discarded and <MvIMPORT> reads the next line. No processing is done on the discarded data.
The result of this <MvIMPORT> with the data file movies.dat would be:
Title Director Year 1. Strange Days Kathryn Bigelow 1995Terminating the Import
Importing can be halted explicitly inside an <MvIMPORT> loop using the <MvIMPORTSTOP> tag. The program jumps to the code following the </MvIMPORT> tag, without reading any more input from the data file.
Using the Record Number (recno)
The variable recno, containing to the current line number in the file being read, is generated automatically by <MvIMPORT>. A variable named recno is also generated and updated when the program is navigating in a database, and refers to the current record number in that database. If you are using both <MvIMPORT> and a database at the same time, you should disambiguate the two recno variables by using g.recno to refer to the <MvIMPORT> recno, and db_alias.d.recno (where db_alias is the database alias) to refer to the database recno.
Nested Imports
It is possible in Miva Script to nest <MvIMPORT> tags. For example, you could have an 'outer' <MvIMPORT> that reads in a list of data file names from a 'master' data file, and an 'inner' <MvIMPORT> that, for each iteration of the outer <MvIMPORT>, reads the records of the data file whose name was read. The value of recno always refers to the record number in the current nesting level.
Writing to External Files
<MvEXPORT>
Writes a single line of data to an external output file.
- <MvEXPORT
- FILE="filename"
- FIELDS="var1,var2,var3,..."
- DELIMITER="chars">
Writes the values of variables var1, var2, var3,..., to the external file filename, separated by the characters chars. FILE, FIELDS, and DELIMITER are all required attributes. DELIMITER can be one or more characters. <MvEXPORT> is an empty tag. For example:
- <MvEXPORT
- FILE="workers.dat"
- FIELDS="name,title,dept,salary"
- DELIMITER="##">
This tag will write a line such as the following to the file workers.dat:
Frank Flash##V.P. of Silence##Admin##120000
When <MvEXPORT> writes a line of data, it always adds it to the end of the output file, and it always starts on a new line.
Unlike <MvIMPORT>, <MvEXPORT> does not loop automatically. If you want to write multiple output lines, you will have to put the <MvEXPORT> tag inside an <MvWHILE> loop.
If the data file named by the FILE attribute does not exist, then it is created when the <MvEXPORT> tag is executed.
File Locking
<MvLOCKFILE>
<MvLOCKFILE> indicates to other processes that the current process has requested an exclusive lock on filename (required). A lock request is in effect until the corresponding </MvLOCKFILE> end-tag is reached or the process ends or times out. Multiple lock requests with <MvLOCKFILE> on the same file are queued. There is no limit on the number of files on which lock requests can be made. filename can be any file--a database, memo, index, or flat (.dat,.txt, etc.) file. <MvLOCKFILE> tags can be nested.
- <MvLOCKFILE FILE="filename">
- ...
- </MvLOCKFILE>
Note: In this section, it is useful to make a distinction between a 'program' and a 'process'. The same program (file containing Miva Script code) can be running more than once simultaneously on the same server: each such instance of a running program is referred to as a 'process'. Often, an instance of a program will be 'competing' with other instances of the same program (that is, other processes) for access to the same files.
<MvLOCKFILE> provides a method for the currently running program to indicate to other processes (which could be other running programs, or other instances of the same program) that it has requested exclusive access to a file. It is important to understand that a single <MvLOCKFILE> by itself does not cause the file to be locked: rather, if two processes are using <MvLOCKFILE>, and one process requests a lock, and then the second process requests a lock on the same file, then the second process is prevented from continuing until the first process's lock request is removed. However, if the second process does not issue an <MvLOCKFILE> before attempting to access the file in question, there is nothing to prevent this access from taking place. For this reason, we refer to the action of <MvLOCKFILE> as a lock request, rather than a lock: it is a request to other Miva Script processes that they 'respect' the current process's request for exclusive access. Another way of looking at this is: <MvLOCKFILE> does not interact with any other Miva Script tag, only other <MvLOCKFILE>s.
Even though <MvLOCKFILE> does not provide absolute security against a file being accessed by another process, it is still a useful technique: if you code an application properly <MvLOCKFILE> will provide protection against other instances of the same application accessing a file simultaneously.
Typically you would not have a lock request in place during the entire execution of your program, as this might unnecessarily block other processes from accessing the file in question. You need to identify blocks of code during whose execution it would be desirable to have a lock request in place. Then you surround the blocks of code with <MvLOCKFILE> and </MvLOCKFILE> tags.
- <MvLOCKFILE FILE="workers.txt">
- <MvEXPORT FILE="workers.txt"...>
- </MvLOCKFILE>
In this example, the program requests a lock on the text file workers.txt while an <MvEXPORT> is performed.
If a lock request exists on a file, and another process requests a lock on that file (with <MvLOCKFILE>), then the second lock request is queued until the first lock request is removed. When the lock request is removed, the second process's lock request becomes the 'primary' one. While the second process is waiting, its execution stops; if the process has to wait a long time, this could cause it to time out. Several lock requests can be queued at once.
Note: <MvLOCKFILE> uses a 'semaphore file' mechanism. The name of the semaphore file is not guaranteed be the same when Miva Script is used on different platforms, or to remain the same in future implementations of Miva Script. For this reason, programs should not be written in a way that relies on the existence of a specific semaphore file name.
Nesting <MvLOCKFILE>
You can nest two more <MvLOCKFILE>...</MvLOCKFILE> tag pairs if you need to lock two different files at the same time. For example:
- <MvLOCKFILE FILE="input.dat">
- <MvLOCKFILE FILE="output.dat">
- ...
- </MvLOCKFILE>
- </MvLOCKFILE>
The <MvLOCKFILE> and </MvLOCKFILE> tags are paired: the innermost </MvLOCKFILE> removes the lock request created by the innermost <MvLOCKFILE> (in this example, the lock request on output.dat).
You should take care to avoid the situation known as a "deadly embrace": this occurs when a process makes a lock request on a file for which the same process already has a lock request in place. For example:
- <MvLOCKFILE FILE="foo.dat">
- ...
- <MvLOCKFILE FILE="foo.dat">
- ...
- </MvLOCKFILE>
- ...
- </MvLOCKFILE>
When the second <MvLOCKFILE> is executed, the process stops executing while it waits for the lock request on foo.dat to be removed; however, this will never happen, precisely because the current process, which is also the process that issued the first lock request, is now stopped! The process will wait until it eventually times out.
Note: If a process terminates without explicitly removing a lock request on a file, it will be removed automatically the next time an <MvLOCKFILE> requests a lock on the same file.
Database Locking
As indicated in the database sections, when you are adding (<MvADD>) or updating (<MvUPDATE>) a database record, Miva automatically prevents the same record from being updated by more than one process at the same time. No other Miva Script process can access a record while it is locked. However, it can not be guaranteed which update or add will occur first.
<MvLOCKFILE> is neither required nor desirable to provide record-level locking when <MvADD> and <MvUPDATE> are used. But, other database operations-<MvPACK>, <MvMAKEINDEX>, and <MvREINDEX>-do not perform automatic locking. You may wish to use <MvLOCKFILE> to provide these tags with added data integrity (you should lock both the database and index files in question); however, since each of these tags tend to be time-consuming, we recommend that you use them 'off-line', thereby avoiding both data integrity issues and excessive resource consumption on your server.
| Join Our Mailing List | Privacy Policy | Store Policies | Contact Us |
| © Copyright 2008 Miva Merchant. All Rights Reserved. |