Thursday, December 31, 2015

Android soft key menu 보유 여부 확인

    public boolean hasSoftKeys() {
        boolean hasSoftwareKeys = true;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            final Display d = getWindowManager().getDefaultDisplay();

            final DisplayMetrics realDisplayMetrics = new DisplayMetrics();
            d.getRealMetrics(realDisplayMetrics);

            final int realHeight = realDisplayMetrics.heightPixels;
            final int realWidth = realDisplayMetrics.widthPixels;

            final DisplayMetrics displayMetrics = new DisplayMetrics();
            d.getMetrics(displayMetrics);

            final int displayHeight = displayMetrics.heightPixels;
            final int displayWidth = displayMetrics.widthPixels;

            hasSoftwareKeys = (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0;
        } else {
            boolean hasMenuKey = ViewConfiguration.get(this).hasPermanentMenuKey();
            boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
            hasSoftwareKeys = !hasMenuKey && !hasBackKey;
        }
        return hasSoftwareKeys;
    }

Sunday, December 27, 2015

Android Volley와 Gson 예제

AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>

build.gradle
    compile 'com.google.code.gson:gson:2.5'
    compile 'com.github.bumptech.glide:volley-integration:1.3.1'

mt_rand.php
<?php
$ret = array('value' => mt_rand()+1000000000);
echo json_encode($ret);
?>

    public class Uid {
        private String value;
        public String getValue() {
            return value;
        }
    }
    private RequestQueue mRequestQueue;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String url ="http://192.168.29.188/mt_rand.php";

        JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d("MainActivity", response.toString());

                        Uid uid = new Gson().fromJson(response.toString(), Uid.class);

                    }
                },
                new Response.ErrorListener() {

                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.d("MainActivity", error.toString());
                    }
                }
        );

        mRequestQueue = Volley.newRequestQueue(this);
        mRequestQueue.add(request);
    }

Tuesday, December 22, 2015

MySQL C API에서 /var/lib/mysql/mysql.sock으로 접속이 안될때

MySQL Server를 설치한다.
yum -y install mysql mysql-server (or apt-get install mysql mysql-server)

Enable the MySQL service:
/sbin/chkconfig mysqld on

Start the MySQL server:
/sbin/service mysqld start

vi /etc/my.cnf
아래와 같이 클라이언트를 추가한다.
이미 있다면 수정하여 준다.

 [client]
 #password       = your_password
 host            = 127.0.0.1
 port            = 3306
 socket          = /var/run/mysql/mysql.sock

socket을 /var/lib/mysql/mysql.sock로 수정해주고
host를 127.0.0.1 또는 localhost로 수정해준다.
host는 여기에 입력한 대로 mysql_connect에서 호출해야 한다.(localhost로 입력했으면 127.0.0.1로는 접속안됨)

Monday, December 21, 2015

iOS 디바이스 UUID 획득하기

//처음에 UUID를 KeyChain에서 불러오는데 nil이라면 UUID를 생성해서 KeyChain에 저장한다.
//저장 후에 다시 함수를 호출 하면 저장된 값을 리턴한다.
NSString* getUUID()
{
    // initialize keychaing item for saving UUID.
    KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"UUID" accessGroup:nil];
   
    NSString *uuid = [wrapper objectForKey:(__bridge id)(kSecAttrAccount)];
   
    if( uuid == nil || uuid.length == 0)
    {
        // if there is not UUID in keychain, make UUID and save it.
        CFUUIDRef uuidRef = CFUUIDCreate(NULL);
        CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
        CFRelease(uuidRef);
        uuid = [NSString stringWithString:(__bridge NSString *) uuidStringRef];
        CFRelease(uuidStringRef);
       
        // save UUID in keychain
        [wrapper setObject:uuid forKey:(__bridge id)(kSecAttrAccount)];
    }
   
    return uuid;
}

Android 디바이스 UUID 획득하기

public static String getDeviceUUID(final Context context) {
        // preference에서 저장된 것이 있는지 확인해봄
        final SharedPreferences prefs = context.getSharedPreferences("UUIDTEST", MODE_PRIVATE);
        final String id = prefs.getString("UUID", null);

        UUID uuid = null;
        if (id != null) {
            uuid = UUID.fromString(id);
        } else {
            final String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
            try {
                // 예전에 아래와 같은 고정값이 리턴되는 문제가 있었음
                if (!"9774d56d682e549c".equals(androidId)) {
                    uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));
                } else {
                    final String deviceId = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
                    uuid = deviceId != null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")) : UUID.randomUUID();
                }
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }

            final SharedPreferences.Editor editor = prefs.edit();
            editor.putString("UUID", uuid.toString());
            editor.commit();
        }

        return uuid.toString();
    }

Friday, December 18, 2015

Android 플레이스토어 앱설치시 '콘텐츠 제공자의 권한이 중복됩니다' 및-505 에러 유저 대응 방법

플레이스토어 앱설치시 '콘텐츠 제공자의 권한이 중복됩니다' 및 -505 에러가 난 유저에게는 다음과 같이 안내하면 된다.

1. 설정 > 앱(LG) 또는 애플리케이션 관리자(삼성) > 전체로 이동
2. Google Play 스토어 > 캐쉬 삭제 > 데이터 삭제
3. Google Play 서비스 > 캐쉬 삭제 > 데이터 삭제
4. 설정 > 계정 > Google > 기존에 사용하는 구글 계정 삭제
5. 디바이스 재부팅
6. 설정 > 계정 > Google > 다시 기존에 사용하는 구글 계정 추가
7. Play 스토어에서 앱 다시 다운로드

Thursday, December 17, 2015

Tuesday, December 15, 2015

Mac 기본 웹서버 apache root 폴더

/Library/WebServer/Documents
/etc/apache2/httpd.conf
sudo /usr/sbin/apachectl restart

Monday, December 14, 2015

iOS 리뷰 유도를 위해서 앱스토어로 이동하기

#define URL_APPSTORE @"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=xxxxxxxx&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"

            // 앱스토어로 이동
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:URL_APPSTORE]];

Android 리뷰 유도를 위해서 플레이스토어로 이동하기

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + Config.getPackageName()));

Sunday, December 13, 2015

Ubuntu The mcrypt extension is missing. PHP

php5-fpm과 nginx환경에서 phpmyadmin을 실행과정에서 다음과 같은 에러가 발생했다.
The mcrypt extension is missing. Please check your PHP configuration.

해결방법은 다음과 같다.
sudo apt-get install php5-mcrypt
sudo php5enmod mcrypt
sudo service php5-fpm restart
sudo service nginx restart

CentOS nginx phpMyAdmin 설치


# 확장 패키지(EPEL)가 있는지 확인
rpm -qa epel-release

# 확장 패키지 설치
yum install epel-release

# phpMyAdmin 설치
yum install phpMyAdmin

# 설치폴더
/usr/share/phpMyAdmin

# 폴더 심볼릭 링크 생성
cd /usr/share/nginx/html
ln -s /usr/share/phpMyAdmin phpMyAdmin

CentOS nginx + php에서 file not found 에러

Nginx와 php연동시 File not found 에러가 나는 경우가 있다.
이때는 nginx설정에서 fastcgi_param에서 다음과 같이 html폴더를 절대경로로 명시해 주면된다.

vi /etc/nginx/conf.d/default.conf

    location ~ \.php$ {
        #root           /usr/share/nginx/html;
        #fastcgi_pass   127.0.0.1:9000;
        fastcgi_pass    unix:/var/run/php-fpm/www.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /usr/share/nginx/html$fastcgi_script_name;
        include        fastcgi_params;
    }

그리고 nginx restart하자.
service nginx restart

CentOS nginx iptables 설정


CentOS에서 nginx를 설치하고 나면 80번 포트가 막혀있다.
그래서 다음과 같이 iptables를 편집해서 80번 포트를 열어야 한다.

sudo vi /etc/sysconfig/iptables

# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

service iptables restart


Visual C++ 11자리 정수형 유니크 ID 만들기

정수형으로 유저ID로 사용할 유니크ID를 만들어 보았다.
32비트 UINT_MAX는 0xFFFFFFFF로 4,294,967,295가되며 약 42억9천만이 된다.
그리고 rand()를 사용하면 리턴값은 int지만 사실상 short인 SHRT_MAT 32767이 최대값이다.
따라서 다음과 같이 만들면 long형인 11자리 값을 만들수가 있다.

srand(time(NULL));
unsigned hirand = rand() << 16;
unsigned lorand = rand();

//앞에 1또는 다른 값을 붙여 써도 됨
printf("1%010u\n", hirand | lorand);

Saturday, December 12, 2015

Android 네이버 앱스토어에서 결제 실패로 반려되는 경우

네이버 앱스토어는 자사의 결제를 이용하지 않은 구글 플레이스토어 결재만을 탑재한 앱의 APK를 올리는 것을 허용하고 있다.

이때 유의할 것이, 플레이스토어의 앱 업데이트가 먼저 배포된 다음에 네이버 앱스토어의 심사를 진행해야 한다는 것이다. 왜냐면 아래에 언급한 문제가 있기 때문이다.

Android 앱에서 플레이스토어 결제가 되지 않는 경우

그리고 일반적으로 구글의 심사 속도보다 네이버의 심사속도가 느리다. 그러므로 항상 구글의 심사가 완벽히 끝난 다음에 네이버 앱스토어의 심사를 진행하자.

Android 앱에서 플레이스토어 결제가 되지 않는 경우

플레이스토어에 올라간 앱의 경우, 스토어의 최신 버전보다 높은 version number를 가진 앱의 경우에는 결제가 실패한다.

업데이트 대기중인 앱의 결제 테스트가 실패하는게 그 원인이다. 따라서 앱의 결제 테스트를 할때는 스토어의 최신버전으로 맞춰놓고 결제 테스트를 하고, 실제 테스트는 플레이스토어 배포를 한후에 테스트를 하자.

Ubuntu ssh 설치

우분투 desktop은 외부에서 접속하는 ssh가 기본적으로 설치되어 있지 않다.
그래서 다음과 같이 설치하면 22번으로 ssh가 설치되고 자동으로 서비스가 시작된다.

sudo apt-get install openssh-server

Thursday, December 10, 2015

Mac 패러랠즈 10에서 우분투 15.10 설치기

오랫만에 리눅스가 필요해서 패러랠즈 10에다가 우분투 15.10을 설치였더니 설치가 잘되었다.

하지만 parallels tools가 설치가 되지 않아서 14.04로 다운그레이드 했다. 가상 시디를 넣으면 한자가 깨지는 것 같은 파일만 보이게 되기 때문이다.

패러랠즈를 11로 업데이트를 해야되는 시점이 온것 같다.

Sunday, December 6, 2015

Android 플레이스토어 앱설치시 '콘텐츠 제공자의 권한이 중복됩니다'

구글 플레이스토어에서 앱을 설치시 '콘텐츠 제공자의 권한이 중복됩니다' 에러 메세지가 나타날 경우가 있다.

이 문제는 앱의 Facebook SDK를 버전업 하였을때 provider의 이름 설정과 export 설정이 잘못되어서 다른 앱과 충돌할때 나타난다.

아래의 exported를 반드시 false로 해야지 앱자체의 provider로만 동작한다. 왜냐면 exported를 true로 하면 provider name이 다른 Facebook SDK를 사용한 앱의 provider와 같기 때문이다.

    <provider android:authorities="com.facebook.app.FacebookContentProvider0123456789..."
        android:name="com.facebook.FacebookContentProvider0123456789..."
        android:exported="false" />


Android 인증 SMS 자동인증

<uses-permission android:name="android.permission.RECEIVE_SMS" />

// 폰번호를 인식하여 문자를 보내달라고 요청
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        final TelephonyManager tm = (TelephonyManager) getActivity()
                .getSystemService(Context.TELEPHONY_SERVICE);
        if(tm == null)
            return;

        // 내 폰번호를 받음
        String myNumber = tm.getLine1Number();
        if(StringUtils.isEmpty(StringUtils.trim(myNumber)))
            return;

        // KT일경우 폰번호 변환
        if(myNumber.startsWith("+82")) {
           myNumber = myNumber.replace("+82", "0");
        }
        _phone.setText(myNumber);

        _useSmsReceiver = true;
        sendVerification();
    }
});

private void sendVerification() {
    // 인증번호란에 바로 입력할수 있도록 포커스를 줌
    _code.requestFocus();


    // SMS 인증문자를 바로 수신해서 결과를 입력해줌
    if(_useSmsReceiver)
        registerReceiver();
}

private void registerReceiver() {
    unregisterReceiver();

    final IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(SMSReceiver.ACTION);

    _receiver = new SMSReceiver();
    registerReceiver(_receiver, intentFilter);
}
private void unregisterReceiver() {
    if(_receiver != null) {
        unregisterReceiver(_receiver);
        _receiver = null;
    }
}

private class SMSReceiver extends BroadcastReceiver {
    private static final String TAG="SMSReceiver";
    private static final String ACTION = "android.provider.Telephony.SMS_RECEIVED";

    @Override
    public void onReceive(Context context, Intent intent) {
        // 문자만 수신받음
        if(intent.getAction().equals(ACTION)) {
            final Bundle bundle = intent.getExtras();

            if(bundle == null)
                return;

            final Object[] pdusObj = (Object[])bundle.get("pdus");
            if(pdusObj == null)
                return;

            final SmsMessage[] smsMessages = new SmsMessage[pdusObj.length];

            for(int i=0; i<smsMessages.length; i++) {
                smsMessages[i] = SmsMessage.createFromPdu((byte[])pdusObj[i]);

                final String address = smsMessages[i].getOriginatingAddress();
                final String message = smsMessages[i].getDisplayMessageBody();

                if(address.equals(CS_NUMBER)) {
                    String adjustMessage = message.replace("[Web발신]", "");
                    int begin = adjustMessage.indexOf('[');
                    int end = adjustMessage.indexOf(']');

                    // 인증번호를 찾았다
                    final String codeMessage = adjustMessage.substring(begin+1, end);

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            _code.setText(codeMessage);
                            confirmVerification();
                        }
                    });

                    unregisterReceiver();
                }
            }
        }
    }
}

Android 채팅 EditText 자동 크기 확장 및 멀티라인

<EditText
android:id="@+id/message_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="3dp"
android:hint="@string/chat_room_message_hint"
android:paddingLeft="5dp"
android:textSize="13sp"
android:inputType="textMultiLine"
android:scrollbars="vertical"
android:singleLine="false"
android:minLines="2"
android:maxLines="5"
/>

Android Toast 폰트 변경 및 single line 만들기

// 토스트 폰트 통일
final LinearLayout toastLayout = (LinearLayout) toast.getView();
final TextView toastTV = (TextView) toastLayout.getChildAt(0);

toastTV.setTypeface(Typeface.createFromAsset(getAssetManager(), "NanumGothic.otf"));

if(singleLine) {
    toastTV.setSingleLine(true);
    toastTV.setEllipsize(TextUtils.TruncateAt.END);
}

toast.show();

Visual C++ 파일 열기 다이얼로그 API

char szFile[256];
  //API file dialog
  OPENFILENAMEA ofn;
  ZeroMemory(&ofn, sizeof(ofn));
  ofn.lStructSize = sizeof(ofn);
  ofn.hwndOwner = NULL;
  ofn.lpstrFile = szFile;
  ofn.lpstrFile[0] = '\0';
  ofn.nMaxFile = sizeof(szFile);
  ofn.lpstrFilter = "All\0*.*\0Map\0*.map\0";
  ofn.nFilterIndex = 1;
  ofn.lpstrFileTitle = NULL;
  ofn.nMaxFileTitle = 0;
  ofn.lpstrInitialDir = NULL;
  ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  BOOL ret = GetOpenFileNameA(&ofn);
  if (ret) {
   // load map and enter training mode
  }

Wednesday, December 2, 2015

Linux epoll example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>

#define MAXEVENTS 64

static int
make_socket_non_blocking(int sfd) {
    int flags, s;
    flags = fcntl(sfd, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl");
        return -1;
    }
    flags |= O_NONBLOCK;
    s = fcntl(sfd, F_SETFL, flags);
    if (s == -1) {
        perror("fcntl");
        return -1;
    }
    return 0;
}

static int
create_and_bind(char *port) {
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int s, sfd;
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;     /* Return IPv4 and IPv6 choices */
    hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */
    hints.ai_flags = AI_PASSIVE;     /* All interfaces */
    s = getaddrinfo(NULL, port, &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        return -1;
    }
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (sfd == -1)
            continue;
        s = bind(sfd, rp->ai_addr, rp->ai_addrlen);
        if (s == 0) {
            /* We managed to bind successfully! */
            break;
        }
        close(sfd);
    }
    if (rp == NULL) {
        fprintf(stderr, "Could not bind\n");
        return -1;
    }
    freeaddrinfo(result);
    return sfd;
}

int
main(int argc, char *argv[]) {
    int sfd, s;
    int efd;
    struct epoll_event event;
    struct epoll_event *events;
    if (argc != 2) {
        fprintf(stderr, "Usage: %s [port]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    sfd = create_and_bind(argv[1]);
    if (sfd == -1)
        abort();
    s = make_socket_non_blocking(sfd);
    if (s == -1)
        abort();
    s = listen(sfd, SOMAXCONN);
    if (s == -1) {
        perror("listen");
        abort();
    }
    efd = epoll_create1(0);
    if (efd == -1) {
        perror("epoll_create");
        abort();
    }
    event.data.fd = sfd;
    event.events = EPOLLIN | EPOLLET;
    s = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
    if (s == -1) {
        perror("epoll_ctl");
        abort();
    }
    /* Buffer where events are returned */
    events = calloc(MAXEVENTS, sizeof event);
    /* The event loop */
    while (1) {
        int n, i;
        n = epoll_wait(efd, events, MAXEVENTS, -1);
        for (i = 0; i < n; i++) {
            if ((events[i].events & EPOLLERR) ||
                (events[i].events & EPOLLHUP) ||
                (!(events[i].events & EPOLLIN))) {
                /* An error has occured on this fd, or the socket is not
                   ready for reading (why were we notified then?) */
                fprintf(stderr, "epoll error\n");
                close(events[i].data.fd);
                continue;
            }
            else if (sfd == events[i].data.fd) {
                /* We have a notification on the listening socket, which
                   means one or more incoming connections. */
                while (1) {
                    struct sockaddr in_addr;
                    socklen_t in_len;
                    int infd;
                    char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
                    in_len = sizeof in_addr;
                    infd = accept(sfd, &in_addr, &in_len);
                    if (infd == -1) {
                        if ((errno == EAGAIN) ||
                            (errno == EWOULDBLOCK)) {
                            /* We have processed all incoming
                               connections. */
                            break;
                        }
                        else {
                            perror("accept");
                            break;
                        }
                    }
                    s = getnameinfo(&in_addr, in_len,
                                    hbuf, sizeof hbuf,
                                    sbuf, sizeof sbuf,
                                    NI_NUMERICHOST | NI_NUMERICSERV);
                    if (s == 0) {
                        printf("Accepted connection on descriptor %d "
                                       "(host=%s, port=%s)\n", infd, hbuf, sbuf);
                    }
                    /* Make the incoming socket non-blocking and add it to the
                       list of fds to monitor. */
                    s = make_socket_non_blocking(infd);
                    if (s == -1)
                        abort();
                    event.data.fd = infd;
                    event.events = EPOLLIN | EPOLLET;
                    s = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);
                    if (s == -1) {
                        perror("epoll_ctl");
                        abort();
                    }
                }
                continue;
            }
            else {
                /* We have data on the fd waiting to be read. Read and
                   display it. We must read whatever data is available
                   completely, as we are running in edge-triggered mode
                   and won't get a notification again for the same
                   data. */
                int done = 0;
                while (1) {
                    ssize_t count;
                    char buf[512];
                    count = read(events[i].data.fd, buf, sizeof buf);
                    if (count == -1) {
                        /* If errno == EAGAIN, that means we have read all
                           data. So go back to the main loop. */
                        if (errno != EAGAIN) {
                            perror("read");
                            done = 1;
                        }
                        break;
                    }
                    else if (count == 0) {
                        /* End of file. The remote has closed the
                           connection. */
                        done = 1;
                        break;
                    }
                    /* Write the buffer to standard output */
                    s = write(1, buf, count);
                    if (s == -1) {
                        perror("write");
                        abort();
                    }
                }
                if (done) {
                    printf("Closed connection on descriptor %d\n",
                           events[i].data.fd);
                    /* Closing the descriptor will make epoll remove it
                       from the set of descriptors which are monitored. */
                    close(events[i].data.fd);
                }
            }
        }
    }
    free(events);
    close(sfd);
    return EXIT_SUCCESS;
}

Tuesday, December 1, 2015

PHP CDKEY 쿠폰번호 생성 소스

<?php


//지정된 자릿수의 랜덤한 숫자를 반환합니다. 최대 10까지 가능합니다. 4 이면 1000 에서 9999 사이의 랜덤 숫자
function get_rand_number($len=4) {


    $len = abs((int)$len);
    if ($len < 1) $len = 1;
    else if ($len > 10) $len = 10;


    return rand(pow(10, $len - 1), (pow(10, $len) - 1));
}


//넘어온 세자리수를 36진수로 변환해서 반환합니다. preg_match_callback 을 통해서만 사용됩니다.
function get_simple_36($m){


    $str = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $div = floor($m[0] / 36);
    $rest = $m[0] % 36;


    return $str[$div] . $str[$rest];
}


//지정된 자리수에 존재하는 소수 전체를 배열로 반환합니다. max len = 5
function get_simple_prime_number($len=5){

    $len = abs((int)$len);
    if ($len < 1) $len = 1;
    else if ($len > 5) $len = 5;

    $prime_1 = Array(1, 2, 3, 5, 7);

    if ($len == 1) return $prime_1;

    $start = pow(10, ($len - 1)) + 1;//101
    $end = pow(10, $len) - 1;//999
    $prime = $prime_1;

    unset($prime[0]);//1제거
    unset($prime[1]);//2제거
    $array = Array();
    for($i = 11; $i <= $end; $i+=2){//10보다 큰 소수에는 짝수가 없다.

        $max = floor(sqrt($i));
        foreach($prime as $j) {

            if ($j > $max) break;
            if ($i % $j == 0) continue 2;
        }

        $prime[] = $i;
        if ($i >= $start) $array[] = $i;
    }

    return $array;
}


//지정된 자릿수의 숫자로된 시리얼을 반환합니다. - 를 포함하고 싶지 않을때는 $cut 이 $len 보다 크거나 같으면 됩니다. max len = 36
 function get_serial($len=16, $cut=4, $hipen='-'){


    $len = abs((int)$len);
    if ($len < 1) $len = 16;
    else if ($len > 36) $len = 36;


    $cut = abs((int)$cut);
    if ($cut < 1) $cut = 4;
    else if ($cut > $len) $cut = $len;


    list($usec, $sec) = explode(' ', microtime());
    $base_number = (string)$sec . str_replace('0.', '', (string)$usec);
    $base_number .= (string)get_rand_number(10) . (string)get_rand_number(8);//36자리 유니크한 숫자 문자열

 


    $prime = get_simple_prime_number(5);//5자리 소수 배열
    shuffle($prime);
    $prime = $prime[0];//랜덤한 5자리 소수


    $serial = bcmul(substr($base_number, 0, $len), $prime);
    $serial_length = strlen($serial);
    $sub = $len - $serial_length;


    if ($sub > 0) $serial .= (string)get_rand_number($sub);
    else if ($sub < 0) $serial = substr($serial, 0, $len);


    return preg_replace("`(.{" . $cut . "})`", "$1" . $hipen, $serial, floor(($len-1) / $cut));
 }


//지정된 자릿수의 숫자와 영문으로된 시리얼을 반환합니다. - 를 포함하고 싶지 않을때는 $cut 이 $len 보다 크거나 같으면 됩니다. max len = 24
 function get_serial_mix($len=16, $cut=4, $hipen='-'){


    $len = abs((int)$len);
    if ($len < 1) $len = 16;
    else if ($len > 24) $len = 24;


    $cut = abs((int)$cut);
    if ($cut < 1) $cut = 4;
    else if ($cut > $len) $cut = $len;


    $len2 = (int)($len * 3 / 2);
    if ($len2 % 2 == 1) $len2 += 1;


    $serial = get_serial($len2, $len2, $hipen);


    $serial = substr(preg_replace_callback("`.{3}`", "get_simple_36", $serial), 0, $len);


    return preg_replace("`(.{" . $cut . "})`", "$1" . $hipen, $serial, floor(($len-1) / $cut));
 }


echo get_serial_mix(36, 4, '-');
?>