IT/언어

[PHP] PHP 기초 중의 기초 문법

개발자 두더지 2022. 4. 18. 17:01
728x90

변수


php에서는 데이터형의 선언이 없으며 JavaScript과 같은 var도 없다. 맨 처음에는 $를 붙여서 변수를 선언한다.

$msg = "Hello World";
echo $msg;

$msg이라는 변수에 문자열 "Hello World"를 대입하는 형태이다. echo를 사용하여 변수를 전개하여 Web 페이지에 출력해준다. 이 때의 $msg의 데이터형은 String이다.

PHP에서 다루는 데이터형의 종류는 다음과 같다.

  • 문자를 다룰 때
    • String
  • 숫자를 다룰 때
    • int
    • float
  • 참거짓판단 (true/false)
    • boolean
  • 배열
  • 오브젝트
  • null

데이터형등은 var_dump등을 이용해서 간단히 확인할 수 있다.

$msg = "Hello World";
var_dump($msg);

 

 

정수


 아까 봤던 변수는 뒤에서도 바뀔 가능성이 있는 상자와 같다. 정수는 한 번 정의해두면 나중에 변경하는 것이 안 된다. 정수를 정의할 때는 define이라는 키워드를 사용한다.

define("PI",3.141592);

echo PI;

 이 때 주의할 점이 $가 필요하지 않는다는 것이다. 정수를 정의할 때에는 모두 대문자로 정의하는 것이 일반적이다.이 PI는 정수이므로, 따로 새로운 수를 정의하려고하면 정의되지 않는다.

PI = 3;

 또한, 정수 중 사전에 정의되어 있는 것들이 있다.

var_dump(__LINE__); //지금 행의 수
var_dump(__FILE__); //이 파일까지의 경로
var_dump(__DIR__);  //디렉토리의 경로

 

 

문자열


 "쌍따옴표" 혹은 '홑따옴표'로 감싸면 문자열로 다룰 수 있다. 차이는 따옴표 안에서 변수를 전개할 수 있느냐 없느냐이다.

$name = "konojunya";

echo "My name is $name";

 이렇게 작성함으로써 쌍따옴표 안에 변수를 전개할 수 있다. 또한, 변수의 전개를 더욱 알게 쉽게 적을 수 있다. 

$name = "konojunya";

echo "My name is $name";
echo "My name is ${name}";
echo "My name is {$name}";

 위 코드의 출력 결과는 모두 동일하다. 그럼 홑따옴표를 써야만하는 상황은 언제일까?

 

 

문자열의 연결


변수를 전개한 것과 문자열을 연결시키면 동일한 결과를 얻을 수 있다. 

$name = "konojunya";

echo 'My name is '.$name;

PHP에서는 .(도트)를 사용하면 문자열을 연결할 수 있다.

 

 

배열


 비슷한 데이터를 하나의 이름으로 관리하고 싶을 때 주로 사용하는 것이 배열이다.

$colors = array("red","blue","yellow");

PHP 5.4 버전 이상이라면 괄호대신 []로 바꿔 쓸 수 있다.

$colors = ["red","blue","yellow"];

 이때 예를 들어 red를 출력하고 싶은 경우 인덱스로 출력하면 된다.

echo $colors[0];

 

 

연관(연상) 배열


 일단 사용법을 살펴보자.

$members = [
  "jun" => "konojunya",
  "hoge" => "hogehogeo"
];

꺼내는 방법도 매우 간단하다.

echo $members["jun"]; //konojunya

 

 

루프(반복문)와 조건 분기(조건문)


 루프(for, while)와 조건 분기(if, switch) 문법 자체는 다른 언어와 동일하므로 문법 자체에 대한 설명은 스킵하도록 하겠다. PHP에서는 HTML과 함께 사용하는 경우가 꽤 있다고 생각된다.

 여기서 아래와 같이 작성하는 것은 꽤 읽기 힘들다.

?><ul>
  <?php for($i=0;$i<10;$i++){ ?>
  <li><?php echo $i ?></li>
  <?php } ?>
</ul>

따라서 클론 구문을 사용해서 바꿔 쓸 수 있다.

?><ul>
  <?php for($i=0;$i<10;$i++): ?>
  <li><?php echo $i ?></li>
  <?php endfor; ?>
</ul>

 처음의 물결 모양 괄호를 : 로 바꾸고, 마지막의 물결 모양 괄호를 endOO으로 바꿔 쓸 수 있다.

 

 

함수


 함수는 인수와 반환값이 존재한다. 먼저 단순히 Hello World를 echo하는 hello 함수를 만들어보자.

function hello(){
  echo "Hello World";
}

hello(); //Hello World

 다른 언어와 동일하다. function을 사용하여 함수를 정의한다. 그러면 이름을 인수로 전달한 경우는 어떻게 되는지 살펴보자.

function hello($name){
  echo "Hello ${name}";
}

hello("junya"); // Hello junya

 junya를 $name으로 대입하여 사용하고 있다. 또한, 이 $name은 이 hello 함수 안에서만 사용되는 로컬 변수이다. 이 함수 밖에서는 $name에 액세스할 수 없다. 

 반환값을 사용하는 경우는 어떻게 될 것인가 ?

function hello($name){
  return "Hello ${name}";
}

echo hello("junya"); // Hello junya

 return이라는 키워드로 이 함수에서 값을 반환하도록 할 수 있다.

 

 

클래스


간단하게 작성법에 대해서 살펴보자, 유저를 관리하는 User 클래스를 만들자.

class User{
  //property
  public $name;

  //constructor
  public function __construct($name){
    $this->name = $name;
  }

  //method
  public function sayName(){
    echo "Hello $this->name !";
  }
}

 클래스는 맨 처음 글자를 대문자로 쓴다. 클래스는 property, constructor, method를 가지고 있다.

  • property는 그 클래스가 가지는 값이다.
  • constructor는 그 클래스가 인스턴스화 될 때에 반드시 호출되는 것이다.
  • method는 그 클래스가 가지고 있는 함수라고 생각해주길 바란다.

 이 User 클래스는 $name이라는 프로퍼티를 가지고 있다. 인스턴스화 할 때, construnctor에 의해 그 $name 인수로 들어 온 값이 대입된다.

인스턴스화 할 때는 new라는 키워드를 사용한다.

$junya = new User("junya");

 이것으로 $junya는 자신의 이름이나 sayName()메소드를 사용할 수 있다. 자신의 이름에 액세스하기 위해서는 다음과 같이 작성하며

echo $junya->name;

 sayName()에 액세스하기 위해서는 다음과 같이 쓴다.

$junya->sayName();

 

 

클래스의 상속


 이전에 만들어 놓은 클래스를 조금 변형해서 계속해서 새로운 것으로 만들어서 사용할 때 클래스를 상속하면 된다. 앞에서 만든 User 클래스를 상속받아 SpesialUser이라는 클래스를 만들자.

class User {
  public $name;

  public function __construct($name){
    $this->name = $name;
  }

  public function sayHi(){
    echo "Hello $this->name !";
  }
}

class SpecialUser extends User {
  public function saySpecialHi(){
    echo "I am Special $this->name";
  }
}

 User 클래스를 상속받기 위해서는 extends라는 키워드를 사용한다. 이것으로 Spesial User 클래스는 독자적인 메소드도 사용할 수 있지만, User 클래스의 것들도 사용할 수 있다. SpecialUser 인스턴스할 때도 위에서 봤던 방식과 동일하다 .

$junya = new SpecialUser("junya");

 

Over ride

여기서 User 클래스에 있는 sayHi 메소드를 SpecialUser 클래스에도 작성하면 어떻게 될까?

class User {
  public $name;

  public function __construct($name){
    $this->name = $name;
  }

  public function sayHi(){
    echo "Hello $this->name !";
  }
}

class SpecialUser extends User {
  public function sayHi(){
    echo "I am Special $this->name";
  }
}

 사용해보자.

$junya = User("junya"); //User클래스
$kono = SpecialUser("kono"); //SPUser클래스

$junya->sayHi(); // Hello junya
$kono->sayHi(); // I am Special kono

 동일한 메소드이지만, Special 유저의 kono와 일반 유저 junya의 출력 결과가 다르다는 것을 알 수 있다. 이러한 것을 over ride라고 부른다. 즉 "덮어쓰기"이다. 이 over ride는 편리하긴 하지만, over ride되지 않기를 바라는 경우도 있을 것 이다.

 이 때 사용할 수 있는 것이 final이라는 키워드이다. 아까 User클래스의 sayHi에 final을 사용하여, over ride되지 않도록 해보자.

class User {
  public $name;

  public function __construct($name){
    $this->name = $name;
  }

  final public function sayHi(){
    echo "Hello $this->name !";
  }
}

class SpecialUser extends User {
  public function sayHi(){
    echo "I am Special $this->name";
  }
}

 User 클래스에 final을 붙이는 것으로, SpecialUser의 sayHi 메소드는 over ride에 실패하므로 에러가 발생한다. 

 

 

액세스 권한


 지금가지 클래스 안에서 주로 등장했더 pubilc에 관련된 이야기이다. 이것이 바로 액세스 권한이다. 액세스 권한은 총 세 가지가 있다.

  • public : 어디에서든 액세스 가능하다.
  • protected : 부모 자식 관계 클래스와 클래스 안에서만 액세스 가능하다.
  • private : 클래스 안에서만 액세스 가능하다.
class User {
  public $name;

  public function __construct($name){
    $this->name = $name;
  }

  public function sayHi(){
    echo "Hello $this->name !";
  }
}

class SpecialUser extends User {
  public function sayHi(){
    echo "I am Special $this->name";
  }
}

 이 상태에서는 User 클래스의 $name은 public으로 선언되어 있으므로, 인스턴스화한 $junya에서도 $junya->name으로 액세스 가능했다. 이것을 다음과 같이 바꿨다.

class User {
  private $name;

  public function __construct($name){
    $this->name = $name;
  }

  public function sayHi(){
    echo "Hello $this->name !";
  }
}

class SpecialUser extends User {
  public function sayHi(){
    echo "I am Special $this->name";
  }
}

 private으로 하면 클래스 내에서는 액세스 할 수 있지만, 그외에서는 액세스 할 수 없게 되므로 SpecialUser의 $this->name도 액세스 할 수 없으며, 아까까지 액세스 가능했던 $junya->name도 액세스 할 수 없다. SpecialUser는 허가하도록 하고 싶은 경우는 private가 아닌 protected로 하자 !

 

 

static에 대해서


 static은 클래스를 인스턴스화하지 않아도 사용할 수 있도록 하는 구조이다. 

class User {
  public $name;
  public static $count = 0;
  public function __construct($name){
    $this->name = $name;
    self::$count++;
  }
  public static function getMes(){
    echo "Hello from static!";
  }
}

User::getMes();
$tom = new User("tom");
echo User::$count; //1

static을 붙인 메소드나 프로퍼티는 클래스명::메소드 등 으로 액세스 할 수 있다. User 클래스 안에, 자기 자신을 참고하고 싶은 경우에는 self를 사용하여 self::$count와 같이 사용하면 된다.

 

 

추상 클래스


abstract class BaseUser{ //추상 클래스
  public $name;
  abstract public function sayHi();
}

class User extends BaseUser{
  public function sayHi(){
    echo "Hello";
  }
}

 선언할 때에 abstrac이라는 키워드를 붙여준다. 추상 클래스는 그 자체를 인스턴스화하는 것은 불가능하다. 추상 클래스의 메소드는 그 확장 클래스에서 구현하지 않으면 안된다는 규칙을 부여한다. 구현하지 않으면 에러가 발생한다. 액세스 권한등, 추상 클래스에 맞추지 않아도 에러가 발생한다.

 

 

interface


 그럼, 추상 클래스와 비슷한 interface이라는 것도 있다.

interface sayHi {
  public function sayHi(); //interface는 반드시public
}
interface sayHello{
  public function sayHello();
}

class User implements sayHi,sayHello {
  public function sayHi(){
    echo "Hi!";
  }
  public function sayHello(){
    echo "hello";
  }
}

 class는 커다란 설계도로 비유할 수 있는데에 반해, interface는 부품 하나 하나와 비슷한 느낌이다. interface로 선언한 것도 클래스 안에서 구현하지 않으면 안 된다. 그러나 지금까지의 extends가 아닌, implements이라는 것을 사용한다.

 여기서 크게 다른 것은 추상 클래스는 하나이지만, interfaces는 여러 개 동시에 추가할 수 있다는 점이다. 그러므로 보다 상세한 것(걷기, 말하기)을 구현하는 것은 interface를 사용하고, 커다란 부분(인간, 생물 등)을 정의하는 것은 클래스로 구현하는 것이 적합하지 않을까라는 생각을 한다.

 

 

외부 파일의 읽어들이기


 새롭게, functions.php를 만들자. functions.php에 아래와 같은 내용이 정의되어 있다고 가정하자.

function hello(){
  echo "Hello";
}

.......

 함수를 정의하고 있는 functions.php를 index.php에서 읽어들이도록 해보자.

require "functions.php"; // 이중에 하나
require_once "functions.php";
include "functions.php";
include_once "functions.php";

 이 네 개가 파일을 읽어들이는 키워드이다. require와 include의 차이는 require는 파일명이 틀리면 에러가 발생한다. include는 무시하고 그대로 진행된다. xx_once는 한 번 읽어 들였다면 지나치고, 읽어 들인 적이 없다면 읽는다.

require "functions.php";

hello();

 라이브러리등을 이 방법으로 읽어들일 수 있다. 

 

 

이름 공간


 자신이 만든 것과 다른 사람이 만든 것이 이름이 동일한 경우 발생할 수 있는 문제를 해결해주는 것이 이름 공간이다. 먼저 사용해보자. functions.php의 맨 처음에 다음과 같이 작성하자.

namespace junya\functions;

function hello(){
  echo "Hello";
}

 주의해야할 점은 이름공간의 선언은 맨 처음에 해야한다는 것이다. 이것을 index.php에서 사용해보자.

// use konojunya\functions as func;
// use konojunya\functions

 use를 사용하는 것으로 이 이름공간을 사용할 수 있게 된다. 또한, use konojunya\functions as func는 konojunya\functions이라는 이름을 붙여서 그 안에서는 사용한다는 것이다. use konojunya\functions이라면 맨 뒤의 functions를 포착한다.

 이 이름 공간을 어떻게 사용할까?

require "functions.php";
use konojunya\functions as func;

func\hello();

  와 같이 이 함수의 맨 앞에 use로 지정한 문자를 붙인다. 이것은 라이브러리 등과의 충돌을 피하게 해준다.

 

 

예외 처리


 마지막으로 예외 처리 방법에 대해 확인해보자. 예를 들어 나눗셈하는 division()이라는 함수를 만들고 싶다고 생각하고 있다. 

function division($num1,$num2){
  return $num1 / $num2;
}

echo division(4,2); // 2

 이 함수의 약점은 $num2에 0이 들어왔을 경우이다. 

function division($num1,$num2){
  try {
    if($b === 0){
      throw new Exception("can not use 0");
    }
    return $num1 / $num2;
  } catch (Exception $e) {
    return $e->getMessage();
  }
}

 throw으로 Exception이라는 클래스를 인스턴스화한 것을 catch에 던지는 느낌이다. 그 다음 getMessage()이라는 메소드를 사용하여 Exception을 인스턴스할 때에 인수에 넣은 값을 취득할 수 있다. 


참고자료

https://qiita.com/konojunya/items/492e8114e6bd55344731

728x90