Chủ Nhật, 30 tháng 6, 2013

Change layout for module Admin - ZfcAdmin Module for Zend Framework 2

https://github.com/zf-commons/zfcadmin

Introduction

ZfcAdmin is a minimal admin interface for generic administrative purposes. It is a common screen with navigation that hides behind authentication and authorization.

Installation

ZfcAdmin is enabled to be installed via composer. Load zf-commons/zfc-admin in your composer.json file. You can specify its version (currently only 0.1.0 is recommended) or use dev-master to load the latest version from master. Enable ZfcAdmin in your application.config.php configuration file.
If you do not want to use composer, clone this project (either as a git submodule or not) into ./vendor/ directory.

Usage

ZfcAdmin allows you to create routes under a single parent "admin" route. You can also use it to enable navigation in your admin layout. Furthermore integration of BjyAuthorize and ZfcRbac is provided.
The complete configuration is flexible, so you can update the zfcadmin parent route, its children, the navigation and all default provided view scripts. Read more in the documentation about usage and customization of ZfcAdmin.

Development

ZfcAdmin is currently under development. The authors feel ZfcAdmin is stable enough for production versions and you can always fix your ZfcAdmin version to a specific tag. Feel free to report bugs in the issue tracker or come by on IRC at the Freenode channel #zftalk.

Thứ Bảy, 29 tháng 6, 2013

http://modules.zendframework.com

http://modules.zendframework.com

List Module helpful :

1. https://github.com/ipascual/MyZend_Geolocation
2. https://github.com/ipascual/MyZend_Email
3. https://github.com/Bacon/BaconAuthentication
4. https://github.com/gowsram/GTranslate
5. https://github.com/gowsram/zf2-google-maps-
6. https://github.com/AdminWeb/zf2-module-qrcode
7. https://github.com/Celtico/QuAdmin
8. https://github.com/macnibblet/MCNUser
9. https://github.com/Bacon/BaconQrCode
10. https://github.com/remithomas/rt-gravatar
11. https://github.com/ensemble/EnsembleAdmin
12. https://github.com/Soflomo/Mail
13. https://github.com/manuakasam/SamUser
14. https://github.com/alexshelkov/ApptTwig
15. https://github.com/RWOverdijk/SxMail
16. https://github.com/patrioticcow/Formgen
17. https://github.com/odiaseo/zf2-datagrid
18. https://github.com/coremax/CoreBootstrap
19. https://github.com/RWOverdijk/SxBootstrap
20. https://github.com/mnshankar/form-annotations
21. https://github.com/unjudder/zf2-mail
22. https://github.com/blanchonvincent/SimplePageCrawler
23. https://github.com/zf-fr/zfr-forum
24.  https://github.com/cgmartin/ZF2FileUploadExamples
25. https://github.com/samsonasik/SanSamplePagination


26. https://github.com/Hounddog/DoctrineDataFixtureModule
27. https://github.com/vadim-knyzev/ZendDbMigrations
28. https://github.com/atukai/AtAdmin
29. https://github.com/heartsentwined/zf2-cron
30. https://github.com/heartsentwined/zf2-assetwig
31. https://github.com/Enrise/EnriseRestfulApi
32. https://github.com/atukai/AtWeather
33. https://github.com/atukai/AtBase
34. https://github.com/akrabat/AkrabatSession
35. https://github.com/SocalNick/ScnSocialAuthDoctrineORM
36. https://github.com/delboy1978uk/del-countries-flags
37. https://github.com/zucchi/ZucchiUser
38. https://github.com/zucchi/ZucchiUploader
39. https://github.com/zucchi/ZucchiSecurity
40. https://github.com/zucchi/ZucchiLocale
41. https://github.com/zucchi/ZucchiLayout
42. https://github.com/zucchi/ZucchiDoctrine
43. https://github.com/zucchi/ZucchiBootstrap
44. https://github.com/zucchi/ZucchiAdmin
45. https://github.com/zucchi/Zucchi
46. https://github.com/bgallagher/BgOauthProvider
47. https://github.com/raykolbe/DOMPDFModule


48. https://github.com/mickaelCOLLET/MiklSeo
49. https://github.com/artur-gajewski/GoogleWeather
50. https://github.com/cderue/GoogleMaps
51. https://github.com/gabriel403/G403Translator
52. https://github.com/juriansluiman/SlmMail
53. https://github.com/juriansluiman/SlmGoogleAnalytics
54. https://github.com/samsonasik/SanRestful
55. https://github.com/samsonasik/SanCaptcha
56. https://github.com/juriansluiman/SlmLocale
57. https://github.com/weierophinney/PhlyMongo
58. https://github.com/SocalNick/ScnSocialAuth
59. https://github.com/ZF-Commons/ZfcUserDoctrineMongoODM
60. https://github.com/ZF-Commons/ZfcFacebook
61. https://github.com/ZF-Commons/ZfcUserDoctrineORM
62. https://github.com/mwillbanks/ZfcTwitterBootstrap
63. https://github.com/ZF-Commons/ZfcTwig
64. https://github.com/webino/ZF2NetteDebug
65. https://github.com/ZF-Commons/ZfcAdmin
66. https://github.com/ZendExperts/ZeTheme
67. https://github.com/ZendExperts/ZeSecurity
68. https://github.com/zendframework/ZendDeveloperTools
69. https://github.com/silvester/ReverseForm
70.https://github.com/silvester/ReverseOAuth2



Thứ Sáu, 28 tháng 6, 2013

Configuring Poedit for Zend Framework Projects

NOTE :
- Preference -> Editor -> Behaviour -> checkbox Automatically compile .mo file on save
- Also, the source on Github appears to say that the setting is on by default:

poedit / src / resources / prefs.xrc

<object class="wxCheckBox" name="compile_mo">
                        <label>Automatically compile .mo file on save</label>
                        <checked>1</checked>
                      </
object>




There are a few steps you need to take to con­fig­ure poedit to work with a Zend Frame­work project prop­erly. I will take you through the con­fig­u­ra­tion process step by step, and in the end you should have a work­ing installation.
In this tuto­r­ial we are on Win­dows, but the process is the same on Mac & Linux based sys­tems, and poedit even looks much the same on all platforms.
Install poedit and start it, if it’s the first time you run it you should now see a Pref­er­ences dialog.

Per­son­al­ize:
Your name & email — Fill these in
Edi­tor:
You can leave all the options as their defaults, includ­ing the Line end­ings for­mat [Unix]
Trans­la­tion Mem­ory:
Leave this as is for now.
Parsers:
Select PHP and click Edit.



Make sure your dia­log matches the one above exactly!
Now click OK twice and you are done with the preferences.
The main poedit win­dow will now come up,  click File -> New Cat­a­log, you should now see a set­tings window.
Project Info:
Fill in your Project name and ver­sion and the rest of the fields mak­ing sure you select Charset and Source code charset to UTF8 and select­ing the lan­guage and coun­try of the trans­la­tion you are going to cre­ate, in my case Lan­guage: Swedish and Coun­try: SWEDEN.



Now select the Paths tab, and add your projects base path. In my case C:\Zend\Apache2\htdocs\testbench then click the New item tool and add; appli­ca­tion



Now select the Key­words tab and click the New item tool and add;
  • trans­late
  • _
  • set­La­bel
  • set­Value
  • setMes­sage
  • setLe­gend
  • _refresh
  • append
  • prepend


(Note: If you have any other key­words that come to mind, feel free to com­ment and I’ll add them to this tutorial)
Now you click OK and the Save as dia­log comes up move to your project appli­ca­tion direc­tory and select or cre­ate the lan­guages direc­tory the path should look some­thing like C:\Zend\Apache2\htdocs\testbench\application\languages and save the file as sv_SE.po (replace this with the language/locale code that you have choosen.)
Now your source code will be scanned after the key­words you spec­i­fied ear­lier and the Update Sum­mary dia­log will be show­ing all the strings it detected;



In this exam­ple the strings where caught from;
$this->headTitle()->prepend($this->translate('TestBench Application -'));
<?php echo $this->translate("Welcome %s, your last login was %s",$this->user['name'],$this->user['active']); ?>
in my layouts/scripts/layout.phtml file.
When you click OK on the Update Sum­mary Dia­log you will be taken to the main poedit win­dow where you can trans­late the strings.



As you can see it’s very easy to work with sim­ply enter your trans­la­tions in the bot­tom text box.
Now after you are done you sim­ply click File -> Save and two files will be writ­ten to your lan­guages direc­tory, in my case sv_SE.po and sv_SE.mo where the .mo file is the com­piled ver­sion that Zend_Translate uses.
Now if you add new strings to your source code you sim­ply load poedit and open your sv_SE.po file and select Cat­a­log -> Update from sources and it will again show you the Update Sum­mary dia­log with all new string as well as changed strings and removed (Obso­lete) strings.
There are a ton of good Zend_Translate ref­er­ences out there, google is your friend!

Change langue in Zend 2

    'service_manager' => array(
        'factories' => array(
            'translator' => 'Zend\I18n\Translator\TranslatorServiceFactory',
        ),
        'abstract_factories' => array(
            'Zend\Cache\Service\StorageCacheAbstractServiceFactory',
            'Zend\Log\LoggerAbstractServiceFactory',
        ),
    ),
    'translator' => array(
        'locale' => 'en_US',
        'translation_file_patterns' => array(
            array(
                'type'     => 'gettext',
                'base_dir' => __DIR__ . '/../language',
                'pattern'  => '%s.mo',
            ),
        ),
    ),



AND
class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $eventManager        = $e->getApplication()->getEventManager();
        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach($eventManager);
       
        $translator = $e->getApplication()->getServiceManager()->get('translator');
        $translator->setLocale("fr_FR");
    }
}

Thứ Năm, 27 tháng 6, 2013

Create .po language files using PoEdit in Zend 2

Create .po language files using PoEdit

In this post i will instruct you guys on how to create your very own translation files for your ZF2-Modules using PoEdit. I wasn’t able to find any information at all on how to create language files from scratch. All i was able to find were information on how to edit existing ones. If your google skills are as bad as mine, i hope you somehow found this post for me to help you out. Let’s take a look on how to create language files from scratch.

Set up PoEdit for ZF2 View Fiels

By default PoEdit doesn’t know how to parse ZF2′s view files. We first need to tell the program to actually look for and parse them. This is done by following these simple steps:
  • Download PoEdit
  • Install and Open PoEdit
  • GoTo Menu File > Settings > Parser
  • Select PHP > Edit
  • Add ;*.phtml to file extensions to parse
  • Hit OK twice

Create the language files

Now let’s take a look at why PoEdit is so much referenced throughout the internet when it comes to generating translation files. We will automate the process for our very own translation files.
  • Goto Menu File > New Catalog
  • Feel free to edit the Information in the window, but leave Language blank
  • GoTo Tab Paths
  • Add the path to your Module as default path, e.g.:
        C:\xampp\htdocs\AwesomeApp\modules\MyModule
  • Add . as an alternate path
  • GoTo Tab Keywords
  • Add the ZF2 translator keyword “translate” (obviously no brackets)
  • Hit OK
  • Select your save-path, e.g.:
        C:\xampp\htdocs\AwesomeApp\modules\MyModule\language
  • Define the file name, e.g.: ‘de_DE’.po
  • Hit OK
That’s it. Now PoEdit will look through all .phtml-files and search for the translate function. All values passed to the view helper will be referenced in the .po file and you’ll be able to edit translations for your file.
One thing to note: When you wish to add or remove single translation string when making newer versions of your Module, simply open the language file, e.g.: de_DE.po and select Catalog > Refresh from sources
PS: I only have PoEdit on a german version. I assume that my guessed translation for menu items will be very similar but probably won’t match 1:1. Please help me out and leave a comment for me to be able to update this post for greater accuracy. Thanks in advance.

resources :  http://samminds.com/2012/09/create-po-language-files-using-poedit/

Multi Language with zend 2

Zend Framework 2 Translate, I18n, Locale

Translanslation in ZF2 is handled pretty easily and one has several ways to do it. In this blog i will follow the ZendSkeletonApplications approach by using translation files in *.mo-format. How do we create those files? How do we tell the application to use the files? What options do we have to set the locale to use? I will answer all these questions for you to internationalize your modules.

Use translations in your view-scripts

Before we create our language (translation) files, let’s first take a look at what we have at our hands to actually use the translation. In ZF2 we have a couple of i18n view helpers available to use. Let’s take a look at an example that I’ve created in a project recently.
In this view script you see three different translation view helpers used. The default translate(), the currencyFormat() and the dateFormat(). This is the most basic usage of view helpers and with this, you will be able to do a couple of things, but you will run into trouble when you create your own .mo/.po files.

Set up your zf2 module for translation

Let’s now take a look at the steps we have to do to set up translation for our modules. First thing would be to ensure that we have a path available for our translation files to be stored into. I suggest going with the suggested layout from the ZendSkeletonApplication to make your Modules as accessible to other developers as possible.
Now that we’ve taken care of that, let’s configure our module to load translation files from said directory.
Please take a quick look at the highlighted lines. First one would be to set your modules namespace into your configuration file. Personally i do this a lot, as it helps in quite a few configuration issues. Makes Copy&Paste’ing a little easier for future modules ;)
The important part however are lines 12-14. Here we define three things.
  • The directory to load translation files from
  • How the files are named
  • The Text-Domain of the translation
Pretty much like what is done within the ZendSkeletonApplication isn’t it? Not really. text_domain ??? This is the part where i had my problems, too. Luckily Ludwig_ from #zftalk@freenode.net was able to help me out. Each translation file that is loaded needs to have a text_domain added. If no text_domain is added, ‘default’ will be assumed. One might think that this is no problem, but actually there can only be one translation file for each text_domain. Since there can be only one text_domain i choose my modules __NAMESPACE__ to be the text_domain, too. As that should pretty much be unique. With this configuration in place, the language files will be loaded.
Now we actually need to change our view-script, too. The translate()-view-helper needs to know about the text_domain, too. Once again i’ll go with the __NAMESPACE__ to make it a little easier for me to type.

Set up locale in ZF2

When working with I18n-Features, we have several ways to set up the locale to use. One way would be to hard code this in our views like the following.
It’s quite obvious that this is more than unlikely to be a handy solution and will probably only be used in very rare cases where you have to ensure a very general name not to get translated. So we have to look for another option. We can set up a locale at the configuration files, too. For this we need to add the locale key to our translator configuration.
Once again this feature kind of sucks. We need to make the locale variable. For this we have several features, too. Personally i like to go with the clients HTTP_ACCEPT_LANGUAGE. Of course you could extend this by first going with the HTTP_ACCEPT_LANGUAGE and later check if there’s a session variable set (or some user preferences at the database), for now let’s just go with it though. Time to edit our Module.php
Within our modules Bootstrap we now set our default Locale to that of the HTTP_ACCEPT_LANGUAGE. I’d argue that in a lot of cases this is a good thing and doesn’t need to be changed often. As mentioned previously, you could potentionally look up $_SESSION-Variable or DB-Entries for user-specific Locale-settings and overwrite the current locale to make it better.
In case no valid locale can be built from the HTTP_ACCEPT_LANGUAGE (you never know what those hackers do!) we still have a fallback defined, in this example ‘en_US’.

All set and done, let’s resume

Let’s take a quick look at the steps we’ve taken to internationalize our Module.
  1. We’ve set up a folger for language files
  2. We’ve set up configuration to load language files from said folder
  3. We’ve set up the translator to load a Locale depending on users HTTP_ACCEPT_LANGUAGE
  4. Furthermore we know how to force specific Locale-Settings
  5. We’ve learned about the text_domain
  6. We’ve also learned about how to use translation features in our view-scripts
Now there is only one thing left to do and that is to actually create the language files. As this is a little off-topic i have created a separate post for this. Please take a look at Using PoEdit to create Translation files. It’s a very easy and straight-forward process that doesn’t consume much time at all.
All in all i hope that i was able to let you in on how to internationalize your modules. If you encoutner any errors, let me know about it and i will try my best to help you out. Just drop me a comment and we’ll see where it goes from there.

ERROR: Class Intl | IntlDateFormatter not found…

A thing i forgot to mention earlier was, that you really need to have the intl-extension running on your webserver. Linux users should find the official documentation helpful enough. Windows users should locate their respective php.ini and search for ‘intl’, remove the semikolon ‘;’ from the line with the extension
PHP
1
2
3
//... lots of other extensions
extension=php_intl.dll
//... lots of extensions

 
INSTALL LCOATION PACKAGE FOR PHP :
Ubuntu : 
You need to have installed the intl PHP extension.
For APT based systems, try: sudo apt-get install php5-intl
For YUM based systems, try: sudo yum install php-intl
 
 

Authentication and ACL in Zend 2


1. http://samsonasik.wordpress.com/2012/10/23/zend-framework-2-create-login-authentication-using-authenticationservice-with-rememberme/

2.  https://github.com/samsonasik/SanAuth

3. http://samsonasik.wordpress.com/2012/10/04/zend-framework-2-step-by-step-build-form-using-annotation-builder/

4. https://github.com/ZF-Commons/ZfcAcl

5. https://github.com/dollyaswin/zf2-authentication

6. http://p0l0.binware.org/index.php/2012/02/18/zend-framework-2-authentication-acl-using-eventmanager/

7. http://www.tutorial-portal.com/tutorial/50/login---authentication

8. http://www.chrisweldon.net/blog/2012/06/25/restful-web-services-authentication-in-zend-framework/

9. http://thebestsolution.org/zend-authentication-with-facebook-twitter-and-google/

Thứ Hai, 24 tháng 6, 2013

Detect visitor’s country with PHP & MySQL

Resources : http://www.jameslittle.me.uk/detect-visitors-country-with-php-mysql/

Of course you don’t have to use PHP, or MySQL for that matter. But it’s my method of choice for most web apps, and it’s also a pretty common one. The general gist is to do a lookup on a database of geographical locations for IP addresses, having taken your visitor’s IP address from the PHP superglobal array $_SERVER. Yes there are caveats: the database is not 100% complete/accurate, and some ISPs (like AOL!) use proxies across different countries so the user will appear to come from somewhere other than their true country of origin. Boo hoo, let’s do it anyway; according to MaxMind, their free(!) GeoLite Country database is 99.3% accurate, and their licensed version, 99.8%.

The database is released monthly in CSV format, so I’ll have to import it into MySQL using mysqlimport, or LOAD DATA INFILE. I prefer the first option. Those of you that are MySQL fans probably know that there is a CSV storage engine available, but that’s only in version 5.1 which is still in Release Candidate stage, so I’ll stick to mysqlimport.

Download the GeoLite database from Maxmind, extract the CSV file and rename it to something more convenient; mysqlimport uses the filename for the name of the MySQL table it imports into:
root@jim-desktop:/home/jim/data# unzip GeoIPCountryCSV.zip
root@jim-desktop:/home/jim/data# mv GeoIPCountryWhois.csv geo_csv.csv

Before we import the data into MySQL we need to create a table for it to go into. The following DDL accurately describes the structure of the data. Obviously create a new database if you want; here I have one called geo_ip:
CREATE TABLE  `geo_ip`.`geo_csv` (
 `start_ip` char(15) NOT NULL,
 `end_ip` char(15) NOT NULL,
 `start` int(10) unsigned NOT NULL,
 `end` int(10) unsigned NOT NULL,
 `cc` char(2) NOT NULL,
 `cn` varchar(50) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1

If you look at the data in the CSV file you’ll see it’s delimited by commas and the text is qualified by double quotes. With that in mind, we use the following statement to import the data:

root@trivan-destop:/data4tri/LOCATION# mysqlimport -uroot -ppassword --fields-terminated-by=","  --fields-optionally-enclosed-by="\"" --lines-terminated-by="\n" geo_ip geo_csv.csv
mysqlimport: Error: 13, Can't get stat of '/var/lib/mysql/geo_ip/geo_csv.csv' (Errcode: 2), when using table: geo_csv
root@trivan-destop:/data4tri/LOCATION# cd /var/lib/mysql/geo_ip/
root@trivan-destop:/var/lib/mysql/geo_ip# ls
db.opt  geo_csv.frm  geo_csv.MYD  geo_csv.MYI
root@trivan-destop:/var/lib/mysql/geo_ip# cp /data4tri/LOCATION/geo_csv.csv geo_csv.csv
root@trivan-destop:/var/lib/mysql/geo_ip# ls
db.opt  geo_csv.csv  geo_csv.frm  geo_csv.MYD  geo_csv.MYI
root@trivan-destop:/var/lib/mysql/geo_ip# mysqlimport -uroot -ppassword --fields-terminated-by=","  --fields-optionally-enclosed-by="\"" --lines-terminated-by="\n" geo_ip geo_csv.csv
geo_ip.geo_csv: Records: 79980  Deleted: 0  Skipped: 0  Warnings: 0
root@trivan-destop:/var/lib/mysql/geo_ip#


If the mysqlimport binary is not in your environment path, use locate to find it. If you don’t have it at all then use LOAD DATA INFILE.
So we now have the raw data imported into MySQL, but how do we use it? First let’s take a look at the data:
mysql> select * from geo_csv order by rand() limit 10;
 +---------------+----------------+------------+------------+----+----------------+
 | start_ip      | end_ip         | start      | end        | cc | cn             |
 +---------------+----------------+------------+------------+----+----------------+
 | 207.209.7.0   | 207.209.7.255  | 3486582528 | 3486582783 | AU | Australia      |
 | 79.99.200.0   | 79.99.207.255  | 1331939328 | 1331941375 | BE | Belgium        |
 | 217.27.192.0  | 217.27.207.255 | 3642474496 | 3642478591 | DE | Germany        |
 | 194.59.180.0  | 194.59.180.255 | 3258692608 | 3258692863 | FR | France         |
 | 81.16.160.0   | 81.16.175.255  | 1360044032 | 1360048127 | SE | Sweden         |
 | 62.23.198.192 | 62.23.198.207  | 1041745600 | 1041745615 | GB | United Kingdom |
 | 64.49.231.240 | 64.49.232.15   | 1077012464 | 1077012495 | US | United States  |
 | 83.217.68.32  | 83.217.68.95   | 1406747680 | 1406747743 | BE | Belgium        |
 | 91.193.20.0   | 91.193.23.255  | 1539380224 | 1539381247 | CH | Switzerland    |
 | 194.37.249.0  | 194.37.249.255 | 3257268480 | 3257268735 | SE | Sweden         |
 +---------------+----------------+------------+------------+----+----------------+
 10 rows in set (0.22 sec)

The table is essentially a big list (~103k records) of IP ranges, given in both dot-decimal and decimal form. The decimal form is the most efficient to search on as the datatype int requires less memory than char, and with integers we can reliably make use of operators such as greater than, less than, BETWEEN, etc. Exactly how you will use the data will depend on your scenario. I began looking into this when I was working on a German website that wanted to know when a visitor was from Switzerland, so it could display prices in CHF rather than Euros. So in fact, all I needed to know was whether the visitor was Swiss, and if they were from any other country, they would see Euros. So the only columns I’ll need from the table are start and end, and all the rows belonging to Switzerland, or ‘CH’. So to make searches more efficient I’ll grab only the data I need and put it in a new table called ch_ip:
mysql> create table ch_ip as select start,end from geo_csv where cc='CH';
Query OK, 2023 rows affected (0.05 sec)
Records: 2023  Duplicates: 0  Warnings: 0

Great, that’s cut the data from nearly 103 thousand records to just over 2 thousand, and we’ve also lopped off four columns. I’ll now be searching on a table that’s 18K in size, rather than the original 5.3MB. Maybe you need every row of data in your scenario, but in many cases you only need a fraction. And in any case, you really don’t need the start_ip and end_ip columns (as you will see shortly). You could also split off the country names (cn column) into another table so that cc becomes a foreign key. Or you could ditch the country names completely and create an array of CC => CN in your application; there are only 239 unique CCs after all:
mysql> select count(distinct cc) from geo_csv;
 +--------------------+
 | count(distinct cc) |
 +--------------------+
 |                239 |
 +--------------------+
 1 row in set (0.05 sec)

So I have my table of 2,023 Swiss IP ranges. Now I need to grab a visitor’s IP address and convert it into decimal notation. For this we can use PHP’s built-in function ip2long. We use sprintf to ensure the result is always unsigned:
<?php  $ip_num = sprintf("%u", ip2long($_SERVER['REMOTE_ADDR']));  ?>
Once we have $ip_num we can create our MySQL query:
$qry = "SELECT '' FROM ch_ip WHERE $ip_num BETWEEN start AND end";
All we need to know is whether the query returns > 0 rows. If it does, then the visitor is Swiss and we’ll set their locale appropriately. Obviously we don’t want to be performing this query on every page, so once it has been performed once for the visitor we’ll set a session variable. So the final code looks like this:
<?php
session_start();
if (!session_is_registered("locale")) { //check if the session variable has already been set first
    $con = mysql_connect('localhost', 'geo_user', 'geo_password');
    if ($con) {
        $ip_num = sprintf("%u", ip2long($_SERVER['REMOTE_ADDR']));
        mysql_select_db("geo_ip", $con);
        $result = mysql_query( "SELECT '' FROM ch_ip WHERE $ip_num BETWEEN start AND end" );
        $num_rows = mysql_num_rows($result);
        if ($num_rows > 0) {
            $_SESSION['locale'] = "ch";
        }
        else { $_SESSION['locale'] = "de"; }
    }
    else { $_SESSION['locale'] = "de"; //If no db connection can be made then set their locale to German }
};
?>

Học lập trình web căn bản với PHP

Bài 1: Các kiến thức căn bản Part 1:  https://jimmyvan88.blogspot.com/2012/05/can-ban-lap-trinh-web-voi-php-bai-1-cac.html Part 2:  https://...